NOTE: Answers to self-check problems are posted publicly on our web site and are accessible to students. This means that self-check problems generally should not be assigned as graded homework, because the students can easily find solutions for all of them. If you want to assign BJP end-of-chapter problems as homework, please consider using our Exercises or Programming Projects, the solutions to which are not publicly posted (but are available to instructors only by request).
MyProgram.java
is a source code file typed by the programmer, and MyProgram.class
is a compiled executable class file that is run by the computer.
println
, AnnualSalary
, ABC
, sum_of_data
, _average
, and B4
.
System.out.println("Hello, world!");
Output of statements:
"Quotes" Slashes \// How '"confounding' "\" it is!
Output of statements:
name age height Archie 17 5'9" Betty 17 5'6" Jughead 16 6'
Output of statements:
Shaq is 7'1 The string "" is an empty message. \'""
Output of statements:
a b c \\ ' """ C: in he downward spiral
Output of statements:
Dear "DoubleSlash" magazine, Your publication confuses me. Is it a \\ slash or a //// slash? Sincerely, Susan "Suzy" Smith
println
statements to produce desired output:
System.out.println("\"Several slashes are sometimes seen,\""); System.out.println("said Sally. \"I've said so.\" See?"); System.out.println("\\ / \\\\ // \\\\\\ ///");
println
statements to produce desired output:
System.out.println("This is a test of your"); System.out.println("knowledge of \"quotes\" used"); System.out.println("in 'string literals.'"); System.out.println(); System.out.println("You're bound to \"get it right\""); System.out.println("if you read the section on"); System.out.println("''quotes.''");
println
statement to produce desired output:
System.out.println("/ \\ // \\\\ /// \\\\\\");
Equivalent code without System.out.print
statements:
System.out.println("Twas brillig and the "); System.out.println(" slithy toves did gyre and"); System.out.println("gimble"); System.out.println(); System.out.println("in the wabe.");
Output of Commentary
program:
some lines of code have // characters on them which means lines can also have /* and */ characters of comment.
Mistakes in MyProgram
program:
class
is missing.Println
should not be capitalized.
Mistakes in SecretMessage
program:
void
is missing.string
should be capitalized."
mark is missing.}
brace is missing.
Mistakes in FamousSpeech
program:
{
brace is missing.args
is missing.//
comments would fix the problem.)
is missing.public static void example() {
Output of Tricky
program:
This is message1. This is message2. This is message1. Done with message2. Done with main.
Output of Strange
program:
Inside first method Inside third method Inside first method Inside second method Inside first method Inside second method Inside first method Inside third method Inside first method Inside second method Inside first method
Output of Strange2
program:
Inside first method Inside first method Inside second method Inside first method Inside third method Inside second method Inside first method Inside first method Inside second method Inside first method Inside third method
Output of Strange3
program:
Inside second method Inside first method Inside first method Inside second method Inside first method Inside third method Inside first method Inside second method Inside first method
Output of Confusing
program:
I am method 1. I am method 1. I am method 2. I am method 3. I am method 1. I am method 1. I am method 2. I am method 1. I am method 2. I am method 3. I am method 1.
Output of Confusing2
program:
I am method 1. I am method 1. I am method 1. I am method 2. I am method 3. I am method 1. I am method 2. I am method 1. I am method 1. I am method 2. I am method 3.
Output of Confusing3
program:
I am method 1. I am method 2. I am method 1. I am method 1. I am method 2. I am method 3. I am method 1. I am method 1. I am method 2.
Mistakes in LotsOfErrors
program:
LotsOfErrors
(no space).void
should appear after static
.String
should be String[]
.System.println
should be System.out.println
."Hello, world!)
should be "Hello, world!")
.message()
.()
after message.System.out println
should be System.out.println
.cannot";
should be cannot");
."errors"
cannot appear inside a String
. 'errors'
or \"errors\"
would work.}
brace.
Mistakes in Example
program:
Demonstration
) would not match its file name (Example.java
).main
method.main
method would be calling a method (displayRule
) that no longer existed.
Reformatted version of GiveAdvice
program:
// Suzy Student, Fall 2048 // This program prints a message about proper formatting // of Java programs. public class GiveAdvice { public static void main(String[] args) { System.out.println("Programs can be easy or"); System.out.println("difficult to read, depending"); System.out.println("upon their format."); System.out.println(); System.out.println("Everyone, including yourself,"); System.out.println("will be happier if you choose"); System.out.println("to format your programs."); } }
Reformatted version of Messy
program:
// Suzy Student, Fall 2048 // This program prints a cautionary message about messy formatting // of Java programs. public class Messy { public static void main(String[] args) { message(); System.out.println(); message(); } public static void message() { System.out.println("I really wish that"); System.out.println("I had formatted my source"); System.out.println("code correctly!"); } }
int
literals: 22
, -1
, and -6875309
11
Results of int
expressions:
8
11
6
4
33
-16
6.4
6
30
1
7
5
2
18
3
4
4
15
8
1
Results of double
expressions:
9.0
9.6
2.2
6.0
6.0
8.0
1.25
3.0
3.0
3.0
5.0
6.4
37.0
8.5
9.6
4.0
4.8
Results of String
expressions:
11
"2 + 2 34"
"2 2 + 3 4"
"7 2 + 2"
"2 + 2 7"
"(2 + 2) 7"
"hello 34 8"
double grade = 4.0;
int age; String gender; double height; int weight;
String year; int numberOfCourses; double gpa;
number % 10
Mistakes in Oops2
program:
+
between is
and x
.x
has not yet been given any value.x
is being redeclared. The word int
should be omitted.x
is being given a value of the wrong type (double
).+ x
should be outside the quotes.int
should be omitted.and
should be surrounded by quotes.(number % 100) / 10
or (number / 10) % 10
(number % 1000) / 100
or (number / 100) % 10
10
Values of a
, b
, and c
after the code:
a: 6 b: 9 c: 16
Values of first
and second
after the code:
first: 19 second: 8The code swaps the values of the variables
first
and second
.
Rewritten shortened version of the code:
int first = 8, second = 19; first += second; second = first - second; first -= second;
Values of i
, j
, and k
after the code:
i: 4 j: 2 k: 1
Output of code:
46 36 23 13
Expression to compute y
while using *
only four times:
double y = x * (x * (x * (x * 12.3 - 9.1) + 19.3) - 4.6) + 34.2;
Version of ComputePay
program that uses variables to avoid redundant expressions:
public class ComputePay { public static void main(String[] args) { // Calculate my pay at work, based on how many hours I worked each day int totalHours = 4 + 5 + 8 + 4; double salary = 8.75; double pay = totalHours * salary; double taxRate = 0.20; double taxesOwed = pay * taxRate; System.out.println("My total hours worked:"); System.out.println(totalHours); System.out.println("My hourly salary:"); System.out.println("$" + salary); System.out.println("My total pay:"); System.out.println(pay); System.out.println("My taxes owed:"); System.out.println(taxesOwed); } }
// This program computes the total amount owed for a meal, // assuming 8% tax and a 15% tip. public class Receipt { public static void main(String[] args) { int subtotal = 38 + 40 + 30; System.out.println("Subtotal:"); System.out.println(subtotal); double tax = subtotal * .08; System.out.println("Tax:"); System.out.println(tax); double tip = subtotal * .15; System.out.println("Tip:"); System.out.println(tip); double total = subtotal + tax + tip; System.out.println("Total:"); System.out.println(total); } }
public class Count2 { public static void main(String[] args) { for (int i = 1; i <= 4; i++) { System.out.println("2 times " + i + " = " + (2 * i)); } } }
2 * count
15 * count - 11
-10 * count + 40
4 * count - 11
-3 * count + 100
for (int i = 1; i <= 6; i++) { // your code here System.out.println(18 * i - 22); }
Output of oddStuff
method:
4 2
Output of loop:
24 1 22 2 19 3 15 4 10 5
Output of loop:
+---+ \ / / \ \ / / \ \ / / \ +---+
Output of loop:
How many lines How many lines How many lines are printed?
Output of loop:
T-minus 5, 4, 3, 2, 1, Blastoff!
Output of loops:
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50
Output of loops:
* *** ***** ******* ********* *********** ************* *************** ***************** *******************
Output of loops:
****!****!****! ****!****!****!
Output of loops:
************! ************!
Output of loops:
*!*!*!*! *!*!*!*! *!*!*!*! *!*!*!*! *!*!*!*! *!*!*!*!
Mistakes in BadNews
program:
count = count + 2
on line 8 should be moved into the loop header instead of count++
.count
is no longer defined (its scope is limited to the for
loop). It should be declared before the loop begins rather than inside the loop's header.count
should be printed, not count + 2
.MAX_ODD
. One way to fix this would be to write two methods: one to print the odds up to 21 and a second to print the odds up to 11. (Admittedly, this solution is redundant. A better solution to this kind of problem involves parameter passing, which will be demonstrated in later chapters.)
Output of Strange
program:
The result is: 55
2 * line + 2 * SIZE
4 * line + (3 * SIZE)
-line + (2 * SIZE + 3)
Table for output:
line | \ | ! | / |
---|---|---|---|
1 | 0 | 22 | 0 |
2 | 2 | 18 | 2 |
3 | 4 | 14 | 4 |
4 | 6 | 10 | 6 |
5 | 8 | 6 | 8 |
6 | 10 | 2 | 10 |
\
and /
: 2 * line - 2
!
: -4 * line + 26
Table for output:
line | \ | ! | / |
---|---|---|---|
1 | 0 | 14 | 0 |
2 | 2 | 10 | 2 |
3 | 4 | 6 | 4 |
4 | 6 | 2 | 6 |
\
and /
: 2 * line - 2
!
: -4 * line + 18
-4 * line + (4 * SIZE + 2)
public static void example(int x, int y) {
MysteryNums
output:
15 42 10 25
Mistakes in Oops3
program:
y
without declaring and initializing ity
in the method callprinter
without the correct number of parameters (2, in this case)printer
by passing the correct type of parameters (double
, in this case)z
: it is in scope inside printer
, not main
x
main
that were not passed into printer
as a parameter
Output of Odds
program:
1 3 5 1 3 5 7 9 11 13 15 1 3 5 7 9 11 13 15 17 19 21 23 25
Output of Weird
program:
1 2 3 4 5 1 2 3 4 5 6 7 1 2 3 4 number = 8
Output of MysteryNumbers
program:
three times two = 6 1 times three = 28 1 times 1 = 42 three times 1 = 2 1 times eight = 20
Output of MysteryWho
program:
whom and who like it it and him like whom whom and him like him stu and boo like who her and him like who
Output of MysteryTouch
program:
touch your eye to your head touch your head to your eye touch your shoulders to your elbow touch your eyes and ears to your eyes and ears touch your toes to your Toes touch your shoulders to your knees toes
Output of MysterySoda
program:
say coke not pepsi or pop say soda not soda or pepsi say pepsi not koolaid or pop say say not pepsi or pepsi
public static void printStrings(String s, int n) { for (int i = 1; i <= n; i++) { System.out.print(s); } System.out.println(); }
System.out.println
is an overloaded method.
Temperature
program changes the value of its tempc
parameter on line 11, but this doesn't affect the variable tempc
in main
. The (incorrect) output is:
Body temp in C is: 0.0
results of Math
expressions:
1.6
2
36.0
64.0
10.0
116.0
7
5
-5
8.0
11.0
102.0
17.0
20.0
13.0
14.0
Output of MysteryReturn
program:
3 0 1 2 4 4 3 5 2 4 8 1 5 9 4
double grade = 2.7; Math.round(grade); // grade = 2.7 grade = Math.round(grade); // grade = 3.0 double min = Math.min(grade, Math.floor(2.9)); // min = 2.0 double x = Math.pow(2, 4); // x = 16.0 x = Math.sqrt(64); // x = 8.0 int count = 25; Math.sqrt(count); // count = 25 count = (int) Math.sqrt(count); // count = 5 int a = Math.abs(Math.min(-1, -3)); // a = 3
public static int min(int n1, int n2, int n3) { return Math.min(n1, Math.min(n2, n3)); }
public static int countQuarters(int cents) { return cents % 100 / 25; }
Kirk My name is James James Kirk Kirk, Kames T. T. is for Tiberius
results of String
expressions:
13
'a'
'G'
2
"GANDALF THE GRAY"
-1
"o Baggins"
"dalf the GR"
"Goondoolf the GRAY"
"Gandalf the GRAY"
"strange1"
results of String
expressions:
6
19
"q.e.d."
"ARCTURAN MEGADONKEY"
"E."
"egad"
4
1
13
-1
"Arcturan Megadonkeys"
"b"
"Cyber"
"mega Corp"
String quote = "Four score and seven years ago"; String expr1 = quote.substring(5, 10).toUpperCase(); // "SCORE" String expr2 = quote.toLowerCase().substring(0, 4) + quote.substring(20, 26); // "four years"
Hello there. 1+2 is 3 and 1.5 squared is 2.25!There are 10 tokens:
Hello
: (String
)there.
: (String
)1+2
: (String
)is
: (String
)3
: (int
, double
, String
)and
: (String
)1.5
: (double
, String
)squared
: (String
)is
: (String
)2.25!
: (String
)34.50
: The code will run successfully and the variable money will store the value 34.5
.6
: The code will run successfully and the variable money will store the value 6.0
.$25.00
: The code will crash with an exception.million
: The code will crash with an exception.100*5
: The code will crash with an exception.600x000
: The code will crash with an exception.none
: The code will crash with an exception.645
: The code will run successfully and the variable money will store the value 645.0
.Scanner console = new Scanner(System.in); System.out.print("Type an integer: "); int number = console.nextInt(); System.out.println(number + " times 2 = " + (number * 2));
import java.util.*; public class SumNumbers { public static void main(String[] args) { Scanner console = new Scanner(System.in); System.out.print("low? "); int low = console.nextInt(); System.out.print("high? "); int high = console.nextInt(); int sum = 0; for (int i = low; i <= high; i++) { sum += i; } System.out.println("sum = " + sum); } }
Scanner console = new Scanner(System.in); System.out.print("What is your phrase? "); String phrase = console.nextLine(); System.out.print("How many times should I repeat it? "); int times = console.nextInt(); for (int i = 1; i <= times; i++) { System.out.println(phrase); }
Correct syntax to draw a rectangle:
b.g.drawRect(10, 20, 50, 30);
Mistakes in the code:
drawLine
should be made on the Graphics
object g
, not on the DrawingPanel
itself.The following is the corrected code:
DrawingPanel panel = new DrawingPanel(200, 200); Graphics g = panel.getGraphics(); g.drawLine(50, 86, 20, 35);
The black rectangle is being drawn second, so it's covering up the white inner circle. The following code fixes the problem:
DrawingPanel panel = new DrawingPanel(200, 100); Graphics g = panel.getGraphics(); g.setColor(Color.BLACK); g.fillRect(10, 10, 50, 50); g.setColor(Color.WHITE); g.fillOval(10, 10, 50, 50);
The problem is that the parameters for the drawRect and drawLine methods have different meanings.
In drawRect
, the parameters are (x, y, width, height);
in drawLine
, they are (x1, y1, x2, y2).
To fix the problem, the third and fourth parameters passed to drawRect
should be changed to 40 and 20 so that the rectangle's bottom-left corner will be at (50, 40).
The following code fixes the problem:
DrawingPanel panel = new DrawingPanel(200, 100); Graphics g = panel.getGraphics(); g.drawRect(10, 20, 40, 20); g.drawLine(10, 20, 50, 40);
The Draw7
program draws a series of progressively smaller black circles, each with its right and bottom edges touching the right and bottom corners of the window.
Its output looks like this:
English statements translated into logical tests:
z % 2 == 1
z <= Math.sqrt(y)
y > 0
x % 2 != y % 2
y % z == 0
z != 0
Math.abs(y) > Math.abs(z)
(x >= 0) == (z < 0)
y % 10 == y
z >= 0
x % 2 == 0
Math.abs(x - y) < Math.abs(z - y)
Results of relational expressions:
true
false
true
false
true
false
false
true
true
Correct syntax for if
statement:
if (x == y) {
Mistakes in Oops4
program:
if
statement should use ()
parentheses, not {}
brackets=
should be ==
smaller
is out of scope herevoid
should be int
=>
should be >=
(or better yet, no if
test is needed)int
when returning itint smaller
is out of scope here (declare outside if
or return directly)
Output of ifElseMystery1
calls:
Call | Output |
---|---|
a. ifElseMystery1(3, 20); |
13 21 |
b. ifElseMystery1(4, 5); |
5 6 |
c. ifElseMystery1(5, 5); |
6 5 |
d. ifElseMystery1(6, 10); |
7 11 |
Output of ifElseMystery2
calls:
Call | Output |
---|---|
a. ifElseMystery2(10, 2); |
10 6 |
b. ifElseMystery2(3, 8); |
9 9 |
c. ifElseMystery2(4, 4); |
3 4 |
d. ifElseMystery2(10, 30); |
29 30 |
Code to read an integer from the user, then print even
if that number is an even number or odd
otherwise:
Scanner console = new Scanner(System.in); System.out.print("Type a number: "); int number = console.nextInt(); if (number % 2 == 0) { System.out.println("even"); } else { System.out.println("odd"); }
The code incorrectly prints that even numbers not divisible by 3 are odd.
This is because the else
statement matches the most closely nested if
statement (number % 3 == 0
), not the outer if
statement.
The following change corrects the problem. Note the braces around the outer if
statement:
if (number % 2 == 0) { if (number % 3 == 0) { System.out.println("Divisible by 6."); } } else { System.out.println("Odd."); }
The code won't ever print "Mine, too!" because it mistakenly uses the ==
operator to compare two strings.
It should use the equals
method to compare them:
if (name.equals("blue")) { ...
Refactored code to reduce redundancy:
a = 2; if (x < 30) { x++; } System.out.println("Java is awesome! " + x);
Refactored code to reduce redundancy:
Scanner console = new Scanner(System.in); System.out.print("Is your money multiplied 1 or 2 times? "); int times = console.nextInt(); System.out.print("And how much are you contributing? "); int donation = console.nextInt(); sum += times * donation; total += donation; if (times == 1) { count1++; } else if (times == 2) { count2++; }
If the user could type any number, the code might need additional if
statements to increment the proper count variable.
If the user could type anything, even a non-integer, the code might need to use the hasNextInt
method of the Scanner
to ensure valid input before proceeding.
Refactored code to reduce redundancy:
// Prompts for two people's money and reports how many $20 bills are needed. import java.util.*; public class Bills { public static void main(String[] args) { Scanner console = new Scanner(System.in); int numBills1 = getBills(console, "John"); int numBills2 = getBills(console, "Jane"); System.out.println("John needs " + numBills1 + " bills"); System.out.println("Jane needs " + numBills2 + " bills"); } public static int getBills(Scanner console, String name) { System.out.print("How much will " + name + " be spending? "); double amount = console.nextDouble(); System.out.println(); int numBills = (int) (amount / 20.0); if (numBills * 20.0 < amount) { numBills++; } return numBills; } }
Code to read red/green/blue color:
Scanner console = new Scanner(System.in); System.out.print("What color do you want? "); String choice = console.nextLine(); if (choice.equalsIgnoreCase("r")) { System.out.println("You have chosen Red."); } else if (choice.equalsIgnoreCase("g")) { System.out.println("You have chosen Green."); } else if (choice.equalsIgnoreCase("b")) { System.out.println("You have chosen Blue."); } else { System.out.println("Unknown color: " + choice); }
Code to read playing card information:
Scanner console = new Scanner(System.in); System.out.print("Enter a card: "); String rank = console.next(); String suit = console.next(); if (rank.equals("2")) { rank = "Two"; } else if (rank.equals("3")) { rank = "Three"; } else if (rank.equals("4")) { rank = "Four"; } else if (rank.equals("5")) { rank = "Five"; } else if (rank.equals("6")) { rank = "Six"; } else if (rank.equals("7")) { rank = "Seven"; } else if (rank.equals("8")) { rank = "Eight"; } else if (rank.equals("9")) { rank = "Nine"; } else if (rank.equals("10")) { rank = "Ten"; } else if (rank.equals("J")) { rank = "Jack"; } else if (rank.equals("Q")) { rank = "Queen"; } else if (rank.equals("K")) { rank = "King"; } else { // rank.equals("A") rank = "Ace"; } if (suit.equals("C")) { suit = "Clubs"; } else if (suit.equals("D")) { suit = "Diamonds"; } else if (suit.equals("H")) { suit = "Hearts"; } else { // suit.equals("S") suit = "Spades"; } System.out.println(rank + " of " + suit);
The problem with the given sumTo
method is that the sum
variable needs to be declared outside the for
loop.
The following code fixes the problem:
public static int sumTo(int n) { int sum = 0; for (int i = 1; i <= n; i++) { sum += i; } return sum; }
The countFactors
method shown will not compile.
It should count the factors using a a cumulative sum; it should not return inside the loop when it finds each factor.
Instead, it should declare a counter outside the loop that is incremented as each factor is seen.
The following code fixes the problem:
public static int countFactors(int n) { int count = 0; for (int i = 1; i <= n; i++) { if (n % i == 0) { count++; } } return count; }
Code to produce a cumulative product by multiplying together many numbers that are read from the console:
Scanner console = new Scanner(System.in); System.out.print("How many numbers? "); int count = console.nextInt(); int product = 1; for (int i = 1; i <= count; i++) { System.out.print("Next number --> "); int num = console.nextInt(); product *= num; } System.out.println("Product = " + product);
The expression equals 6.800000000000001
rather than the expected 6.8
because the limited precision of the double
type led to a roundoff error.
The expression gpa * 3
equals 9.600000000000001
rather than the expected 9.6
because of a roundoff error.
A fix would be to test that the value is close to 9.6
rather than exactly equal to it, as shown in the following code:
double gpa = 3.2; double diff = Math.abs(gpa * 3 - 9.6); if (diff < 0.1) { System.out.println("You earned enough credits."); }
Output of CharMystery
program:
efg nopqrs qr
Statement that tests to see whether a string begins with a capital letter:
if (Character.isUpperCase(theString.charAt(0))) { ... }
The toLowerCase
method cannot be called on a char
value, which is what the charAt
method returns.
A better solution would be to call the Character.toLowerCase
method on the characters of the string, as shown in the following code:
int count = 0; for (int i = 0; i < s.length(); i++) { if (Character.toLowerCase(s.charAt(i)) == 'e') { count++; } }
Another solution would be to lowercase the entire string once before the loop:
s = s.toLowerCase(); int count = 0; for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == 'e') { count++; } }
The following expression would produce the desired result:
String name = "Marla Singer"; int space = name.indexOf(" "); String lastName = name.substring(space + 1); String firstInitial = name.substring(0, 1); String lastNameFirstInitial = lastName + ", " + firstInitial + "."; System.out.println(lastNameFirstInitial);
Alternatively, you could use this shorter version:
String name = "Marla Singer"; System.out.println(name.substring(name.indexOf(" ") + 1) + ", " + name.charAt(0) + ".");
Code to examine a string and determine how many of its letters come from the second half of the alphabet ('n'
or later):
// assuming that the String is stored in the variable str int count = 0; for (int i = 0; i < str.length(); i++) { if (Character.toLowerCase(str.charAt(i)) >= 'n') { count++; } } System.out.println(count + " letters come after n.");
The preconditions of printTriangleType
method are that the three side lengths constitute a valid triangle. Namely:
The preconditions of the getGrade
method are that the grade parameter's value is between 0 and 100.
The medianOf3
code fails when n3
is the smallest of the three numbers; for example, when the parameters' values are (4, 7, 2), the code should return 4 but instead returns 2.
The method could be correctly written as:
public static int medianOf3(int n1, int n2, int n3) { if (n1 < n2 && n1 < n3) { if (n2 < n3) { return n2; } else { return n3; } } else if (n2 < n1 && n2 < n3) { if (n1 < n3) { return n1; } else { return n3; } } else { // (n3 < n1 && n3 < n2) if (n1 < n2) { return n1; } else { return n2; } } }
or the following shorter version:
public static int medianOf3(int n1, int n2, int n3) { return Math.max(Math.max(Math.min(n1, n2), Math.min(n2, n3)), Math.min(n1, n3)); }
The quadratic
method would fail if the coefficient a
is 0 Invalid values are when a = 0 (because it makes the denominator of the equation equal 0), or if the determinant (b2 - 4ac) is negative (because then it has no real square root).
The following version of the code checks for these cases and throws exceptions:
// Throws an exception if a, b, c are invalid public static void quadratic(int a, int b, int c) { double determinant = b * b - 4 * a * c; if (a == 0) { throw new IllegalArgumentException( "Invalid a value of 0"); } if (determinant < 0) { throw new IllegalArgumentException( "Invalid determinant"); } ... }
The code fails when more than one number is odd, because it uses else if
rather than if
.
The tests should not be nested because they are not mutually exclusive; more than one number could be odd.
The following code fixes the problem:
public static void printNumOdd(int n1, int n2, int n3) { int count = 0; if (n1 % 2 != 0) { count++; } if (n2 % 2 != 0) { count++; } if (n3 % 2 != 0) { count++; } System.out.println(count + " of the 3 numbers are odd."); }
The following version without if
statements also works:
public static void printNumOdd(int n1, int n2, int n3) { int count = n1 % 2 + n2 % 2 + n3 % 2; System.out.println(count + " of the 3 numbers are odd."); }
1 11 21 31 41 51 61 71 81 91
250 250 250 ...
2 4 16
bbbbbabbbbb
10 5 2 1 0 0 0
int n = 1; while (n <= max) { System.out.println(n); n++; }
int total = 25; int number = 1; while (number <= (total / 2)) { total = total - number; System.out.println(total + " " + number); number++; }
int i = 1; while (i <= 2) { int j = 1; while (j <= 3) { int k = 1; while (k <= 4) { System.out.print("*"); k++; } System.out.print("!"); j++; } System.out.println(); i++; }
int number = 4; int count = 1; while (count <= number) { System.out.println(number); number = number / 2; count++; }
Output of mystery
calls:
Call | Output |
---|---|
a. mystery(1); |
1 0 |
b. mystery(6); |
4 2 |
c. mystery(19); |
16 4 |
d. mystery(39); |
32 5 |
e. mystery(74); |
64 6 |
Output of mystery
calls:
Call | Output |
---|---|
a. mystery(19); |
19 0 |
b. mystery(42); |
21 1 |
c. mystery(48); |
3 4 |
d. mystery(40); |
5 3 |
e. mystery(64); |
1 6 |
Range of random values generated:
Code that generates a random integer between 0 and 10 inclusive:
Random rand = new Random(); int num = rand.nextInt(11);
Code that generates a random odd integer between 50 and 99 inclusive.
Random rand = new Random(); int num = rand.nextInt(25) * 2 + 51;
1 11 21 31 41 51 61 71 81 91
count down: 10 count down: 9 count down: 8 count down: 7 count down: 6 count down: 5 count down: 4 count down: 3 count down: 2 count down: 1 count down: 0 count down: -1 ...
250 250 250 ...
100 50
2 4 16
bbbbbabbbbb
10 5 2 1 0 0 0
/\/\/\/\/\/\/\/\
do/while
loop that repeatedly prints a certain message until the user tells the program to stop:
Scanner console = new Scanner(System.in); String response; do { System.out.println("She sells seashells by the seashore."); System.out.print("Do you want to hear it again? "); response = console.nextLine(); } while (response.equals("y"));
public static int zeroDigits(int number) { int count = 0; do { if (number % 10 == 0) { count++; } number = number / 10; } while (number > 0); return count; }
do/while
loop that repeatedly prints random numbers between 0 and 1000 until a number above 900 is printed:
Scanner console = new Scanner(System.in); Random rand = new Random(); int num; do { num = rand.nextInt(1000); System.out.println("Random number: " + num); } while (num < 900);
The printLetters
code has a fencepost problem; it will print a dash after the last letter.
The following code corrects the problem:
public static void printLetters(String text) { if (text.length() > 0) { System.out.print(text.charAt(0)); for (int i = 1; i < text.length(); i++) { System.out.print("-" + text.charAt(i)); } } System.out.println(); // to end the line of output }
Sentinel loop that repeatedly prompts the user to enter a number and, once the number -1
is typed, displays the maximum and minimum numbers that the user entered:
int SENTINEL = -1; System.out.print("Type a number (or " + SENTINEL + " to stop): "); Scanner console = new Scanner(System.in); int input = console.nextInt(); int min = input; int max = input; while (input != SENTINEL) { if (input < min) { min = input; } else if (input > max) { max = input; } System.out.print("Type a number (or " + SENTINEL + " to stop): "); input = console.nextInt(); } if (min != SENTINEL) { System.out.println("Maximum was " + max); System.out.println("Minimum was " + min); }
Results of boolean
expressions:
true
true
false
true
true
false
false
true
true
true
true
false
public static boolean isVowel(char c) { c = Character.toLowerCase(c); // case-insensitive return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'; }or:
public static boolean isVowel(char c) { return "aeiouAEIOU".indexOf(c) >= 0; }
In this isPrime
code the boolean
flag isn't being used properly, because if the code finds a factor of the number, prime
will be set to false
, but on the next pass through the loop, if the next number isn't a factor, prime
will be reset to true
again.
The following code fixes the problem:
public static boolean isPrime(int n) { boolean prime = true; for (int i = 2; i < n; i++) { if (n % i == 0) { prime = false; } } return prime; }
In this contains
code the boolean
flag isn't being used properly, because if the code finds the character, found
will be set to true
, but on the next pass through the loop, if the next character isn't ch
, then found
will be reset to false
again.
The following code fixes the problem:
public static boolean contains(String str, char ch) { boolean found = false; for (int i = 0; i < str.length(); i++) { if (str.charAt(i) == ch) { found = true; } } return found; }
Improved version of startEndSame
code using Boolean zen:
public static boolean startEndSame(String str) { return str.charAt(0) == str.charAt(str.length() - 1); }
Improved version of hasPennies
code using Boolean zen:
public static boolean hasPennies(int cents) { return cents % 5 != 0; }
Output of mystery
calls:
Call | Value Returned |
---|---|
a. mystery(3, 3) |
3 |
b. mystery(5, 3) |
1 |
c. mystery(2, 6) |
2 |
d. mystery(12, 18) |
6 |
e. mystery(30, 75) |
15 |
The Zune code will get stuck in an infinite loop when the current date is the end of a leap year.
On December 31 of a leap year, the days
value will be 366, so code enters the if (isLeapYear)
statement but does not enter the if (days > 366)
statement.
So the loop does not subtract any days and never terminates.
It can be fixed by adding a break
statement to the loop:
int days = getTotalDaysSince1980(); year = 1980; while (days > 365) { // subtract out years if (isLeapYear(year)) { if (days > 366) { days -= 366; year += 1; } else { // new code here break; // new code here } // new code here } else { days -= 365; year += 1; } }
(2 != 3) || (-1 >= 5) || !isPrime(n)
Negations of boolean
expressions:
!b
(x <= y) || (y <= z)
(x != y) && (x > z)
(x % 2 == 0) || !b
(x / 2 != 13) && !b && (z * 3 != 96)
(z >= x) || (z <= y && x < y)
The age/GPA reading code should reprompt for a valid integer for the user's age and a valid real number for the user's GPA. If the user types a token of the wrong type, the line of input should be consumed and the user should be reprompted. The following code implements the corrected behavior:
Scanner console = new Scanner(System.in); System.out.print("Type your age: "); while (!console.hasNextInt()) { console.next(); // throw away offending token System.out.print("Type your age: "); } int age = console.nextInt(); print("Type your GPA: "); while (!console.hasNextDouble()) { console.next(); // throw away offending token System.out.print("Type your GPA: "); } double gpa = console.nextDouble(); System.out.println("age = " + age + ", GPA = " + gpa);
The code will have the following behavior when each value is typed:
Type something for me! Jane
Your name is Jane
Type something for me! 56
Your IQ is 56
Type something for me! 56.2
Your name is 56.2
Code that prompts the user for a number and then prints a different message depending on whether the number was an integer or a real number:
Scanner console = new Scanner(System.in); System.out.print("Type a number: "); if (console.hasNextInt()) { int value = console.nextInt(); System.out.println("You typed the integer " + value); } else if (console.hasNextDouble()) { double value = console.nextDouble(); System.out.println("You typed the real number " + value); }
Write code that prompts for three integers, averages them, and prints the average; robust against invalid input:
String prompt = "Please enter a number: "; Scanner console = new Scanner(System.in); int num1 = getInt(console, prompt); int num2 = getInt(console, prompt); int num3 = getInt(console, prompt); double average = (num1 + num2 + num3) / 3.0; System.out.println("Average: " + average);
Point | y < x |
y == 0 |
count > 0 |
---|---|---|---|
Point A | SOMETIMES | SOMETIMES | NEVER |
ALWAYS | SOMETIMES | SOMETIMES | |
Point C | ALWAYS | ALWAYS | ALWAYS |
Point D | SOMETIMES | SOMETIMES | SOMETIMES |
Point E | NEVER | SOMETIMES | SOMETIMES |
Point | n > b |
a > 1 |
b > a |
---|---|---|---|
Point A | SOMETIMES | SOMETIMES | SOMETIMES |
ALWAYS | SOMETIMES | SOMETIMES | |
Point C | SOMETIMES | ALWAYS | ALWAYS |
Point D | SOMETIMES | ALWAYS | NEVER |
Point E | NEVER | SOMETIMES | SOMETIMES |
Point | next == 0 |
prev == 0 |
next == prev |
---|---|---|---|
Point A | SOMETIMES | ALWAYS | SOMETIMES |
Point B | NEVER | SOMETIMES | SOMETIMES |
Point C | NEVER | NEVER | ALWAYS |
Point D | SOMETIMES | NEVER | SOMETIMES |
Point E | ALWAYS | SOMETIMES | SOMETIMES |
A file is a named collection of information stored on a computer.
We can read a file with a Scanner
using the following syntax:
Scanner input = new Scanner(new File("input.txt"));
The Scanner
should read a new File
with the name test.dat
. The correct line of code is:
Scanner input = new Scanner(new File("test.dat"));
Correct syntax to declare a Scanner to read the file example.txt in the current directory:
b.Scanner input = new Scanner(new File("example.txt"));
Scanner input = new Scanner(new File("input.txt"));
The Scanner
tokenizes the line into:
"welcome...to", "the", "matrix."
The Scanner
tokenizes the lines into:
"in", "fourteen-hundred", "92", "columbus", "sailed", "the", "ocean", "blue", ":)"
There are 17 tokens in the input. The "string" tokens can be read with the next
method. The "integer" tokens can be read with nextInt
. The "real number" tokens can be read with nextDouble
. The tokens are:
Hello
(string)there,how
(string)are
(string)you?
(string)I
(string)am
(string)"very
(string)well",
(string)thank
(string)you.
(string)12
(integer, real number, string)34
(integer, real number, string)5.67
(real number, string)(8
(string)+
(string)9)
(string)"10
" (string)
The file name string should use /
or \\
instead of \
.
The \
is used to create escape sequences, and \\
represents a literal backslash.
The correct string is:
Scanner input = new Scanner(new File("C:/temp/new files/test.dat"));
"numbers.dat"
or "C:/Documents and Settings/amanda/My Documents/programs/numbers.dat"
"data/homework6/input.dat"
or "C:/Documents and Settings/amanda/My Documents/programs/data/homework6/input.dat"
"C:/Documents and Settings/amanda/My Documents/homework/data.txt"
"names.txt"
or "/home/amanda/Documents/hw6/names.txt"
"data/numbers.txt"
or "/home/amanda/Documents/hw6/data/numbers.txt"
"/home/amanda/download/saved.html"
Mistakes in Oops6
program:
main
method must say throws FileNotFoundException
when constructing a file Scanner
new File
when declaring the Scanner
Scanner
for the filenextLine
should be hasNextLine
line
should be nextLine
Scanner
to read the tokens of each linehasNext
should be next()
println
statements to print line/word statsThe following output would be produced:
input: 6.7 This file has input: several input lines. input: input: 10 20 30 40 input: input: test 6 total
Output produced if hasNext
and next
are used:
input: 6.7 input: This input: file input: has input: several input: input input: lines. input: 10 input: 20 input: 30 input: 40 input: test 12 total
Output produced if hasNextInt
and nextInt
are used:
0 total
Output produced if hasNextDouble
and nextDouble
are used:
input: 6.7 1 total
the quick brown fox jumps over the lazy dogb.
the quick brown fox jumps over the lazy dog
Program that prints itself as output:
import java.io.*; import java.util.*; public class PrintMyself { public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("PrintMyself.java")); while (input.hasNextLine()) { System.out.println(input.nextLine()); } } }
Code that prompts the user for a file name and prints the contents of that file to the console as output:
public static void printEntireFile() throws FileNotFoundException { Scanner console = new Scanner(System.in); System.out.print("Type a file name: "); String filename = console.nextLine(); Scanner input = new Scanner(new File(filename)); while (input.hasNextLine()) { System.out.println(input.nextLine()); } }
Program that takes as input lines of text and produces as output the same text inside a box:
// precondition: no line in input is longer than width public static void printBox(Scanner input, int width) { printTopBottom(width); while (input.hasNextLine()) { String line = input.nextLine(); System.out.print("| " + line); for (int i = line.length(); i < width; i++) { System.out.print(" "); } System.out.println(" |"); } printTopBottom(width); } public static void printTopBottom(int width) { System.out.print("+"); for (int i = 0; i < width + 2; i++) { System.out.print("-"); } System.out.println("+"); }
A PrintStream
object is used to write to an external file. It has methods such as println
and print
.
Code to print the following four lines of text into a file named message.txt
:
PrintStream out = new PrintStream(new File("message.txt")); out.println("Testing,"); out.println("1, 2, 3."); out.println(); out.println("This is my output file.");
Code that repeatedly prompts the user for a file name until the user types the name of a file that exists on the system.
public static String getFileName() { Scanner console = new Scanner(System.in); String filename = ""; do { System.out.print("Type a file name: "); filename = console.nextLine(); } while (!(new File(filename).exists())); return filename; }
Code that uses getFileName
before calling printEntireFile
:
// reprompts until file name is valid public static void printEntireFile2() throws FileNotFoundException { String filename = getFileName(); Scanner input = new Scanner(new File(filename)); while (input.hasNextLine()) { System.out.println(input.nextLine()); } }
Syntax to declare an array of ten integers:
e.int[] a = new int[10];
numbers[0]
numbers[9]
or numbers[numbers.length - 1]
int[] data = new int[5]; data[0] = 27; data[1] = 51; data[2] = 33; data[3] = -1; data[4] = 101;
Code that stores all odd numbers between -6 and 38 into an array using a loop:
int[] odds = new int[22]; for (int i = 0; i < 22; i++) { odds[i] = i * 2 - 5; }
After the code is executed, the numbers
array contains the following element values:
[0, 4, 11, 0, 44, 0, 0, 2]
After the code is executed, the data
array contains the following element values:
[3, 3, 0, 0, 6, 9, 0, -18]
The code to print the arrays and to compare them doesn't work properly.
Arrays can't be printed directly by println
, nor can they be compared directly using relational operators such as ==
.
These operations can be done correctly by looping over the elements of each array and printing/comparing them one at a time, or by calling methods of the Arrays
class:
// print the array elements System.out.println(Arrays.toString(first)); System.out.println(Arrays.toString(second)); // see if the elements are the same if (Arrays.equals(first, second)) { ...
Correct syntax to declare an array of six integer values:
a.int[] a = {17, -3, 42, 5, 9, 28};
int[] data = {7, -1, 13, 24, 6};
public static int max(int[] data) { int max = data[0]; for (int i = 1; i < data.length; i++) { if (data[i] > max) { max = data[i]; } } return max; }
public static double average(int[] a) { double mean = 0.0; for (int i = 0; i < a.length; i++) { mean += a[i]; } return mean / a.length; }
An array traversal is a sequential processing of each of an array's elements. Problems that can be solved in this manner include printing an array, comparing two arrays for equality, and searching an array for a given value.
Code that uses a for
loop to print each element of an array named data
:
for (int i = 0; i < data.length; i++) { System.out.println("element [" + i + "] is " + data[i]); }
After the code is executed, the list
array contains the following element values:
[3, 24, 8, -5, 6, 1]
public static void printBackwards(int[] list) { if (list.length == 0) { System.out.println("[]"); } else { System.out.print("[" + list[list.length - 1]); for (int i = list.length - 2; i >= 0; i--) { System.out.print(", " + list[i]); } System.out.println("]"); } }
To make the count
and equals
methods process arrays of String
s, you must change int[]
to String[]
and replace all other occurrences of the word int with String. You must also change any comparisons using ==
to comparisons using the equals
method.
public static boolean allLess(int[] list1, int[] list2) { if (list1.length != list2.length) { return false; } for (int i = 0; i < list1.length; i++) { if (list1[i] >= list2[i]) { return false; } } return true; }
The method to swap array elements works because, unlike integers, arrays are objects and use reference semantics. This means that changes to an array parameter's elements will be seen in the original array by the caller.
Output of ReferenceMystery1
program:
2 [0, 0, 1, 0] 1 [0, 0, 1, 0] 3 [0, 0, 1, 1] 2 [0, 0, 1, 1]
Output of ReferenceMystery2
program:
2 [0, 1] 1 [0, 1] 1 [1, 2] 0 [1, 2]
public static void swapPairs(int[] list) { for (int i = 0; i < list.length - 1; i += 2) { swap(list, i, i + 1); } }
After the code is executed, the numbers
array contains the following element values:
[20, 30, 40, 50, 60, 70, 80, 90, 100, 100]
After the code is executed, the numbers
array contains the following element values:
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
After the mystery
method is executed, the arrays contain the following element values:
a1
: [26, 19, 14, 11, 10]
a2
: [1, 4, 9, 16, 25]
After the mystery2
method is executed, the arrays contain the following element values:
a1
: [1, 3, -3, 13, -4, -24, -6, -14]
a2
: [1, 1, 2, 3, 5, 8, 13, 21]
After the mystery3
method is executed, the array contains the following element values:
[7, 3, 1, 0, 8, 18, 5, -1, 5]
Results of calls to mystery4
method:
0
9
6
8
2
Final array contents after calls to mystery5
method:
[8]
[14, 8]
[7, 2, 3, 3, 1, 4]
[10, 9, 9, 6, 6]
[12, 12, 11, 11, 9, 8]
public static double averageLength(String[] strings) { int sum = 0; for (int i = 0; i < strings.length; i++) { sum += strings[i].length(); } return (double) sum / strings.length; }
public static boolean isPalindrome(String[] array) { for (int i = 0; i < array.length / 2; i++) { if (!array[i].equals(array[array.length - 1 - i])) { return false; } } return true; }
After the code is executed, the numbers
array contains the following element values:
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
Loop to initialize the third row of data to store the numbers 1 through 7:
for (int i = 0; i < 7; i++) { data[2][i] = i + 1; }
Code that constructs a two-dimensional array of integers with 5 rows and 10 columns, filled with a multiplication table:
int[][] table = new int[5][10]; for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { table[i][j] = i * j; } }
Loop to copy the contents of second column into fifth column:
for (int i = 0; i < 6; i++) { matrix[i][4] = matrix[i][1]; }
After the mystery2d
method is executed, the numbers
array contains the following element values:
[[4, 5, 6, 6], [5, 6, 7, 7], [6, 7, 8, 8]]
int[][] jagged = new int[5][]; int value = 1; for (int i = 0; i < 5; i++) { jagged[i] = new int[i + 1]; for (int j = 0; j < i + 1; j++) { jagged[i][j] = value; value++; } }
pixels
, the DrawingPanel
's height is stored as pixels.length
and its width is stored as pixels[0].length
.
public static void toRedChannel(DrawingPanel panel) { Color[][] pixels = panel.getPixels(); for (int row = 0; row < pixels.length; row++) { for (int col = 0; col < pixels[0].length; col++) { // your code goes here pixel = DrawingPanel.getRed(pixels[row][col]); } } panel.setPixels(pixels); }
Procedural programming treats a program as a sequence of actions or commands to perform. Object-oriented programming looks at a program as a group of interacting entities named objects that each keep track of related data and behavior.
An object is an entity that encapsulates data and behavior that operates on the data. A class is the blueprint for a type of object, specifying what data and behavior the object will have and how to construct it.
The state of a String
object is its sequence of characters (which are actually stored internally as an array of char
values).
A String
object's behavior includes its methods, such as length
, substring
, toUpperCase
, and indexOf
.
Output of ReferenceMystery3
program:
14 14 7 9 14 2 18 18 7 9 14 18
The state of a Calculator
object might include the number that has just been computed, as well as another number that is currently being input.
A more complex Calculator
object might also include a memory feature that stores an additional value.
The behavior of a Calculator
object might include methods to add, subtract, multiply, divide, and perhaps carryout other math operations (such as exponentiation, logarithms, and trigonometric functions like sine and cosine).
A field is a variable that exists inside of an object.
A parameter is a variable inside a method whose value is passed in from outside.
Fields have different syntax because they are usually declared with the private
keyword and not in a method's header.
A field's scope is throughout the class, while a parameter's scope is limited to the method.
Name
class that represents a person's name:
// A Name object represents a name such as "John Q. Public". public class Name { String firstName; char middleInitial; String lastName; }
An accessor provides the client access to some data in the object, while a mutator lets the client change the object's state in some way. Accessors' names often begin with "get" or "is", while mutators' names often begin with "set".
Correct syntax for calling computeInterest
method on a BankAccount
object:
double result = acct.computeInterest(42);
// Returns the distance from this point to the given other point. public double distance(Point other) { int dx = x - other.x; int dy = y - other.y; return Math.sqrt(dx * dx + dy * dy); }
Name
class with two methods:
// A Name object represents a name such as "John Q. Public". public class Name { String firstName; char middleInitial; String lastName; // The name in normal order such as "John Q. Public". public String getNormalOrder() { return firstName + " " + middleInitial + ". " + lastName; } // The name in reverse order such as "Public, John Q.". public String getReverseOrder() { return lastName + ", " + firstName + " " + middleInitial + "."; } }
To make the objects of your class printable, define a toString
method in it.
The println
statement is equivalent to the following:
System.out.println(p1.toString());
toString
method for Point
class:
// Returns a String representation of this point, such as "java.awt.Point[x=7,y=2]". public String toString() { return "java.awt.Point[x=" + x + ", y=" + y + "]"; }
toString
method for Name
class:
// Returns a String representation of this Name, such as "John Q. Public". public String toString() { return firstName + " " + middleInitial + ". " + lastName; }or:
// Returns a String representation of this Name, such as "John Q. Public". public String toString() { return getNormalOrder(); }
Finished version of client code:
// construct two Point objects, one at (8, 2) and one at (4, 3) Point p1 = new Point(8, 2); Point p2 = new Point(4, 3); // display the two Point objects' state System.out.println("p1 is " + p1); System.out.println("p2 is " + p2); // display p1 distance from origin System.out.println("p1's distance from origin is " + p1.distanceFromOrigin()); // translate p1 to (9, 4) // translate p2 to (3, 13) p1.translate(1, 2); p2.translate(-1, 10); // display the two Point objects' state again System.out.println("p1 is now " + p1); System.out.println("p2 is now " + p2);
A constructor is a special method that creates an object and initializes its state.
It's the method that is called when you use the new
keyword.
A constructor is declared without a return type.
Two errors with the Point
constructor:
void
keyword in its header, because constructors have no return type.
The header should be:
public Point(int x, int y) {
x
and y
shouldn't have their types redeclared in front of them.
This bug causes shadowing of the fields.
Here are the corrected lines:
x = initialX; y = initialY;
Constructor for Name
class:
// A Name object represents a name such as "John Q. Public". public class Name { String firstName; char middleInitial; String lastName; // Initializes a new Name with the given values. public Name(String initialFirst, char initialMiddle, String initialLast) { firstName = initialFirst; middleInitial = initialMiddle; lastName = initialLast; } // The name in normal order such as "John Q. Public". public String getNormalOrder() { return firstName + " " + middleInitial + ". " + lastName; } // The name in reverse order such as "Public, John Q.". public String getReverseOrder() { return lastName + ", " + firstName + " " + middleInitial + "."; } }
The keyword this
refers to the object on which a method or constructor has been called (sometimes called the "implicit parameter").
It is used to access or set the object's field values, to call the object's methods, or to call one constructor from another.
Constructor for Point
class that copies another point:
// Constructs a Point object with the same x and y // coordinates as the given Point. public Point(Point p) { this.x = p.x; this.y = p.y; }or:
// Constructs a Point object with the same x and y // coordinates as the given Point. public Point(Point p) { this(p.x, p.y); // call the (int, int) constructor }
Abstraction is the ability to focus on a problem at a high level without worrying about the minor details. Objects provide abstraction by giving us more powerful pieces of data that have sophisticated behavior without having to manage and manipulate the data directly.
Items declared public
may be seen and used from any class.
Items declared private
may be seen and used only from within their own classes.
Objects' fields should be declared private
to provide encapsulation, so that external code can't make unwanted direct modifications to the fields' values.
To access private fields, create accessor methods that return their values.
For example, add a getName
method to access the name
field of an object.
Adding setX
and setY
methods to the Point
class:
// Sets this Point's x coordinate to the given value. public void setX(int newX) { x = newX; } // Sets this Point's y coordinate to the given value. public void setY(int newY) { y = newY; }
Encapsulated version of Name
class:
// A Name object represents a name such as "John Q. Public". public class Name { private String firstName; private char middleInitial; private String lastName; // Initializes a new Name with the given values. public Name(String initialFirst, char initialMiddle, String initialLast) { firstName = initialFirst; middleInitial = initialMiddle; lastName = initialLast; } // Returns the person's first name. public String getFirstName() { return firstName; } // Returns the person's middle initial. public char getMiddleInitial() { return middleInitial; } // Returns the person's last name. public String getLastName() { return lastName; } // The name in normal order such as "John Q. Public". public String getNormalOrder() { return firstName + " " + middleInitial + ". " + lastName; } // The name in reverse order such as "Public, John Q.". public String getReverseOrder() { return lastName + ", " + firstName + " " + middleInitial + "."; } }
Mutator methods for Name
class:
// Sets the first name to the given value. public void setFirstName(String firstName) { this.firstName = firstName; } // Sets the last name to the given value. public void setLastName(String lastName) { this.lastName = lastName; } // Sets the middle initial to the given value. public void setMiddleInitial(char middleInitial) { this.middleInitial = middleInitial; }
Encapsulation allows you to change a class's internal implementation without changing its external view to clients. When a class is encapsulated clients cannot directly access its fields, so changing those fields will not disturb client behavior as long as the external view (method behavior) is consistent.
Cohesion is the concept of how well a class's contents go together. You can tell that a class is cohesive when each of its fields stores important state related to the object and each method interacts with that state in some way to produce useful behavior.
We did not place console I/O code into our Stock
class because doing so would force clients to use those exact I/O messages.
By keeping I/O code out of Stock
, we kept it independent from its clients.
Accessor methods for Stock
class:
// Returns this Stock's symbol value. public String getSymbol() { return symbol; } // Returns this Stock's total number of shares purchased. public int getTotalShares() { return totalShares; } // Returns this Stock's total cost for all shares. public double getTotalCost() { return totalCost; }
Code reuse is the practice of writing a single piece of code and using it many times in different programs and contexts. Inheritance is useful for code reuse because it allows you to write a class that captures common useful code and then extend that class to add more features and behavior to it.
Overloading a method involves creating two methods in the same class that have the same name but different parameters. Overriding a method involves creating a new version of an inherited method in a subclass, with identical parameters but new behavior to replace the old.
Correct syntax to indicate that class A
is a subclass of B
:
public class A extends B {
The following statements are marked as legal or illegal:
Vehicle v = new Car(); // legal
Vehicle v = new SUV(); // legal
Car c = new SUV(); // legal
SUV s = new SUV(); // legal
SUV s = new Car(); // illegal
Car c = new Vehicle(); // illegal
The this
keyword refers to the current object, while the super
keyword refers to the current class's superclass.
Use the super
keyword when calling a method or constructor from the superclass that you've overridden, and use the this
keyword when accessing your object's own fields, constructors, and methods.
UndergraduateStudent
can call the setAge
method but cannot directly access the name
or age
fields from Student
.
public UndergraduateStudent(String name) { super(name, 18); year = 0; }
public void setAge(int age) { super.setAge(age); year++; }
Output from the Car
/Truck
statements:
vroom car 1 car 2 vroom truck 1 car 2
Output from the Car
/Truck
statements, version 2:
vroomvroom truck 1 car 1
Output from A
/B
/C
/D
polymorphism code:
B 2 A A 1 D 2 C C 1 A 2 A A 1 A 2 C C 1
Output from Flute
/Blue
/Shoe
/Moo
polymorphism code:
flute shoe 1 flute 2 flute blue 1 flute 2 moo moo 1 moo 2 moo blue 1 moo 2
Output from Flute
/Blue
/Shoe
/Moo
polymorphism code, version 2:
moo 2 blue 1 moo moo 2 moo 1 moo flute 2 shoe 1 flute flute 2 blue 1 flute
Output from Mammal
/SeaCreature
/Whale
/Squid
polymorphism code:
squid creature 1 tentacles BIG! spout creature 2 ocean-dwelling creature 1 creature 2 ocean-dwelling warm-blooded creature 2
Output from Mammal
/SeaCreature
/Whale
/Squid
polymorphism code, version 2:
creature 2 ocean-dwelling creature 1 tentacles squid creature 1 creature 2 ocean-dwelling warm-blooded creature 2 BIG! spout
Output from Bay
/Pond
/Ocean
/Lake
polymorphism code:
Bay 1 Pond 2 Ocean 2 Lake 3 Ocean 2 Pond 1 Pond 2 Pond 3 Pond 1 Pond 2 Lake 3 Pond 2 Bay 1 Pond 2 Bay 2 Lake 3 Bay 2
None of the statements produce errors. Output from Bay
/Pond
/Ocean
/Lake
polymorphism code, version 2:
Bay 1 Pond 2 Bay 1 Pond 2 Ocean 2 Ocean 2 Lake 3 Ocean 2
An is-a relationship is a subclass relationship such as those created by inheritance. A has-a relationship is when one object contains a reference to another as a field.
Having Square
extend Rectangle
is a poor design because a Square
cannot substitute for a Rectangle
.
If the client thinks the Square
is a Rectangle
and calls setWidth
or setHeight
on it, unexpected results will occur.
The client will expect the width and height to be different after the call, but they may not be.
Having each of the 52 playing cards in its own class is not a good design because it will result in a clutter of code files without significant differences between them.
A better design would have one Card
class with fields for rank and suit.
We made DividendStock
a separate subclass from Stock
for two major reasons.
First, not all stocks pay dividends, so it does not make sense for every Stock
object to have a dividends
field and a payDividend
method.
Second, the Stock
code already worked correctly, so we did not want to tamper with it needlessly.
Making DividendStock
a separate class constituted an additive and noninvasive change.
Extending a class causes your class to inherit all methods and data from that class. Implementing an interface forces you to write your own code to implement all the methods in that interface.
The code for class C
must contain implementations of the methods m1
and m2
to compile correctly, because C
claims to implement the I
interface.
What's wrong is that interfaces can't declare fields or write bodies for methods.
The following is a correct Colored
interface:
import java.awt.*; // Represents items that have a color that can be retrieved. public interface Colored { public Color getColor(); }
Extension of Point
class that implements the Colored
interface:
// Represents a point with a color. import java.awt.*; public class ColoredPoint extends Point implements Colored { private Color color; // Constructs a new colored point with the given // coordinates and color. public ColoredPoint(int x, int y, Color color) { super(x, y); this.color = color; } // Returns this point's color. public Color getColor() { return color; } }
Version of Shape
interface with getSideCount
method:
// A general interface for shape classes. public interface Shape { public double getArea(); public double getPerimeter(); public int getSideCount(); }
The following are the implementations of the method in the Circle
, Rectangle
, and Triangle
classes:
// Returns the number of sides a circle has (0). public int getSideCount() { return 0; } // Returns the number of sides a rectangle has (4). public int getSideCount() { return 4; } // Returns the number of sides a triangle has (3). public int getSideCount() { return 3; }
An abstract class is a class intended to be used only as a superclass for inheritance. It's like a normal class in that it can have fields, methods, constructors, and so on. It's different from a normal class in that it can have abstract methods, which are like methods of an interface because only their headers are given, not their bodies. It's also different from a normal class because it can't be instantiated (used to create objects).
You can be sure that the OrderedByLength
class contains a getElement
method and that it implements the arrange
method, because if it extends Ordered
without being abstract
itself, it must have that method in order to compile.
One good design would be to have an abstract superclass named Movie
with data such as name, director, and date.
There would be subclasses of Movie
to represent particular movie types, such as Drama
, Comedy
, and Documentary
.
Each subclass would store its specific data and behavior.
An ArrayList
is a structure that stores a collection of objects inside itself as elements.
Each element is associated with an integer index starting from 0.
You should use an ArrayList
instead of an array if you don't know how many elements you'll need in advance, or if you plan to add items to or remove items from the middle of your dataset.
Correct syntax to construct an ArrayList
to store integers:
ArrayList<Integer> list = new ArrayList<Integer>();
Code to declare an ArrayList
containing ["It", "was", "a", "stormy", "night"]
:
ArrayList<String> list = new ArrayList<String>(); list.add("It"); list.add("was"); list.add("a"); list.add("stormy"); list.add("night");
The list's type is ArrayList<String>
and its size is 5.
Code to insert two additional elements, "dark"
and "and"
, at the proper places:
list.add(3, "dark"); list.add(4, "and");
Code to change the second element's value to "IS"
:
list.set(1, "IS");
Code to remove from the list any strings that contain the letter "a"
:
for (int i = 0; i < list.size(); i++) { if (list.get(i).indexOf("a") >= 0) { list.remove(i); i--; // so the new element i will be checked } }
Code to declare an ArrayList
holding the first 10 multiples of 2:
ArrayList<Integer> numbers = new ArrayList<Integer>(); for (int i = 0; i < 10; i++) { numbers.add(2 * i); }
public static int maxLength(ArrayList<String> list) { int max = 0; for (int i = 0; i < list.size(); i++) { String s = list.get(i); if (s.length() > max) { max = s.length(); } } return max; }
Code to print out whether or not a list of String
s contains the value "IS"
, without using a loop:
System.out.println(list.contains("IS"));
Code to print out the index at which the list contains the value "stormy"
and the index at which it contains "dark"
:
System.out.println(list.indexOf("stormy")); System.out.println(list.indexOf("dark"));
A for-each loop that prints the uppercase version of each String
in the list on its own line:
for (String s : list) { System.out.println(s.toUpperCase()); }
The code throws a ConcurrentModificationException
because it is illegal to modify the elements of an ArrayList
while for-each looping over it.
The code doesn't compile because primitives cannot be specified as type parameters for generic types.
The solution is to use the "wrapper" type Integer
instead of int
.
Change the line declaring the ArrayList
to the following:
ArrayList<Integer> numbers = new ArrayList<Integer>();
A wrapper class is one whose main purpose is to act as a bridge between primitive values and objects.
An Integer
is an object that holds an int
value.
Wrappers are useful in that they allow primitive values to be stored into collections.
Output produced when the mystery1
method is passed each list:
[1, 2, 6, 8]
[10, 30, 40, 20, 60, 50]
[-4, 1, 25, 4, 16, 9, 64, 36, 49]
Output produced when the mystery2
method is passed each list:
[20, 10, 20, 30, 30, 20]
[8, 7, 8, 2, 9, 7, 4, 4, 2, 8]
[33, 28, 33, -1, 3, 28, 17, 9, 33, 17, -1, 33]
Output produced when the mystery3
method is passed each list:
[72, 20]
[1, 20, 18, 15, 11, 6]
[10, 90, 70, 40]
Output produced when the mystery4
method is passed each list:
[31, 21, 11]
[5, 8, 10, 3, 9]
[34, 10, 18, 29, 4, 0]
To arrange an ArrayList
into sorted order, call the Collections.sort
method on it.
For example, if your ArrayList
is stored in a variable named list
, you would call:
Collections.sort(list);
For this to work, the type of the objects stored in the list must be Comparable
.
A natural ordering is an order for objects of a class where "lesser" objects come before "greater" ones, as determined by a procedure called the class's comparison function.
To give your own class a natural ordering, declare it to implement the Comparable
interface and define a comparison function for it by writing an appropriate compareTo
method.
n1.compareTo(n2) > 0
n3.compareTo(n1) == 0
n2.compareTo(n1) < 0
s1.compareTo(s2) < 0
s3.compareTo(s1) > 0
s2.compareTo(s2) == 0
Code that reads two names from the console and prints the one that comes first in alphabetical order:
Scanner console = new Scanner(System.in); System.out.print("Type a name: "); String name1 = console.nextLine(); System.out.print("Type a name: "); String name2 = console.nextLine(); if (name1.compareTo(name2) < 0) { System.out.println(name1 + " goes before " + name2); } else if (name1.compareTo(name2) > 0) { System.out.println(name1 + " goes after " + name2); } else { // equal System.out.println(name1 + " is the same as " + name2); }
Code to read a line of input from the user and print the words of that line in sorted order:
Scanner console = new Scanner(System.in); System.out.print("Type a message to sort: "); String message = console.nextLine(); ArrayList<String> words = new ArrayList<String>(); Scanner lineScan = new Scanner(message); while (lineScan.hasNext()) { words.add(lineScan.next()); } System.out.print("Your message sorted: "); Collections.sort(words); for (String word : words) { System.out.print(word + " "); } System.out.println(); // to end the line of output
You should use a LinkedList
when you plan to add or remove many values at the front or back of the list, or when you plan to make many filtering passes over the list in which you remove certain elements.
The code shown would perform better with an ArrayList
, because it calls the get
method many times using indexes in the middle and end of the list.
This is a slow operation for a LinkedList
.
An iterator is an object that represents a position within a list and enables you to view or make changes to the elements at that position.
Iterators are often used with linked lists because they retain the position in the list, so you don't have to call expensive list methods like get
, add
, or remove
many times on the middle or end of the list.
public static int countDuplicates(LinkedList<Integer> list) { int count = 0; Iterator<Integer> i = list.iterator(); int prev = i.next(); while (i.hasNext()) { int next = i.next(); if (prev == next) { count++; } prev = next; } return count; }
public static void insertInOrder(LinkedList<String> list, String value) { int index = 0; Iterator<String> i = list.iterator(); String next = i.next(); // advance until the proper index while (i.hasNext() && next.compareTo(value) < 0) { next = i.next(); index++; } list.add(index, value); }
public static void removeAll(LinkedList<Integer> list, int value) { Iterator<Integer> i = list.iterator(); while (i.hasNext()) { if (i.next() == value) { i.remove(); } } }
public static void wrapHalf(LinkedList<Integer> list) { int halfSize = (list.size() + 1) / 2; for (int i = 0; i < halfSize; i++) { // wrap around one element int element = list.remove(list.size() - 1); list.add(0, element); } }
An abstract data type defines the type of data a collection can hold and the operations it can perform on that data.
Linked lists implement the List
abstract data type.
public static int countDuplicates(List<Integer> list) { int count = 0; Iterator<Integer> i = list.iterator(); int prev = i.next(); while (i.hasNext()) { int next = i.next(); if (prev == next) { count++; } prev = next; } return count; }
You should use a Set
rather than a List
if you wanted to avoid duplicates or wanted to be able to search the collection quickly.
You should use a TreeSet
when you want to keep the data in sorted natural order.
You should use HashSet
s with non-Comparable
types or when order doesn't matter, to get the fastest searching time.
You can examine every element of a Set
using an iterator or a for-each loop.
After the code executes, the set contains the following elements (in some order):
[32, 90, 9, 182, 29, 12]
To do a union, use the addAll
method to add one set's contents to the other.
To do an intersection, use the retainAll
method to remove elements not common to both sets.
mystery
method is passed each list:
[amanda, helene, jessica]
[riley]
[alex, charlie, phil, roy, tyler]
Code to declare a Map
that associates people's names with their ages:
Map<String, Integer> ageMap = new TreeMap<String, Integer>(); ageMap.put("Stuart", 85); ageMap.put("Marty", 12); ageMap.put("Amanda", 25);
You can examine every key of a Map
by calling the keySet
method and then iterating or for-eaching over the keySet
.
You can examine every value of a Map
by calling the values
method and then iterating or for-eaching over that collection of values, or by looking up each associated value using the keys from the keySet
.
Keys and values contained in the map after the code executes:
{79=Seventy-nine, 8=Ocho, 132=OneThreeTwo, 50=Fifty, 98=Ninety-eight, 86=Eighty-six}
mystery
method is passed each map:
{cinq=five, deux=two, four=quatre, one=un, three=trois}
{board=skate, car=drive, computer=play}
{begin=end, boy=girl, ebert=siskel, first=last, heads=tails}
{cotton=rain, light=tree, seed=tree, tree=violin}
mystery
method is passed each map:
{brick, plaster}
{blue, green, yellow}
{fruit}
{animal, cat, dog, ipl}
mystery
method is passed each pair of maps:
{bar=earth, baz=wind, foo=air, mumble=fire}
{five=quatro, one=dos, three=tres}
{b=years, c=seven, e=ago, g=seven}
The following method implements the new behavior in the WordCount
program:
public static void reverseMap(Map<String, Integer> wordCountMap) { Map<Integer, String> reverseMap = new TreeMap<Integer, String>(); // reverse the original map for (String word : wordCountMap.keySet()) { int count = wordCountMap.get(word); if (count > OCCURRENCES) { reverseMap.put(count, word); } } // print the words sorted by count for (int count : reverseMap.keySet()) { String word = reverseMap.get(count); } }
Recursion is a technique where an algorithm is expressed in terms of itself. A recursive method differs from a regular method in that it contains one or more calls to itself within its body.
A base case is a situation where a recursive method does not need to make a recursive call to solve the problem. A recursive case is a situation where the recursive method does call itself. Recursive methods need both cases because the recursive case is called repeatedly until the base case is reached, stopping the chain of recursive calls.
Output produced by the mystery1
method by each call:
1
1, 2
1, 3
1, 2, 4
1, 2, 4, 8, 16
1, 3, 7, 15, 30
1, 3, 6, 12, 25, 50, 100
Output produced by the mystery2
method by each call:
113
140, 70
168, 84, 42
120, 60, 30
160, 80, 40, 20, 10
Output produced by the mystery3
method by each call:
*
[*]
([*])
([([*])])
[([([*])])]
Output produced by the mysteryXY
method by each call:
4
8, 4, 8
16, 8, 16
12, 8, 4, 8, 12
12, 9, 6, 3, 6, 9, 12
Recursive version of doubleReverse
method:
public static void doubleReverse(String s) { if (s.length() > 0) { char last = s.charAt(s.length() - 1); System.out.print(last); System.out.print(last); doubleReverse(s.substring(0, s.length() - 1)); } }
A call stack is the structure of information about all methods that have currently been called by your program. Recursion produces a tall call stack in which each recursive call is represented.
The new code shown would print the lines in their original order, not reversed.
The new code shown would cause infinite recursion, because each recursive call just makes another recursive call and doesn't progress toward the base case.
The version of the pow
method shown does not have any base case, so the recursive calls will never stop. It can be fixed by adding a check for y == 0
that does not make a recursive call.
The second version of the pow
method is more efficient than the first because it requires fewer recursive calls.
Both versions are recursive.
Value returned by the mystery4
method for each call:
6
4
7
0
1
Value returned by the mystery5
method for each call:
57
1029
-74
2438
132483
Value returned by the mystery6
method for each call:
7
6
4
10
5
Recursive version of factorial
method:
public static int factorial(int n) { if (n == 0) { return 1; } else { return n * factorial(n - 1); } }
The base case if
statement has a bug: It should test for numbers less than 10, not greater.
The following is the correct line:
if (n < 10) {
When the parameters needed for recursion don't match those that make sense for the client to pass, use a public method with the signature desired by the client and a private helper method with the signature needed for the recursion.
The following version of the fibonacci
code has improved efficiency:
public static int fibonacci(int n) { if (n <= 2) { return 1; } else { return fibonacci(n, 3, 1, 1); } } private static int fibonacci(int n, int i, int prev, int curr) { if (n == i) { return prev + curr; } else { return fibonacci(n, i + 1, curr, prev + curr); } }
A fractal is an image that is recursively constructed to contain smaller versions of itself. Recursive methods are useful when drawing fractal images because they can elegantly express the recursive nature of the images.
Code to create and draw a regular hexagon:
public static void drawHexagon(Graphics g, Point position, int size) { Polygon poly = new Polygon(); poly.addPoint(position.x, position.y + size / 2); poly.addPoint(position.x + size / 3, position.y); poly.addPoint(position.x + 2 * size / 3, position.y); poly.addPoint(position.x + size, position.y + size / 2); poly.addPoint(position.x + 2 * size / 3, position.y + size); poly.addPoint(position.x + size / 3, position.y + size); g.drawPolygon(poly); }
Recursion is an effective way to implement a backtracking algorithm because the memory of decisions and points to go back to are represented by the recursive call stack. The pattern of "choose, explore, un-choose is elegantly represented by recursive calls for each individual choice.
A decision tree is a description of the set of choices that can be made by a recursive backtracking method at any point in the algorithm.
Decision tree that would have resulted for Figure 12.9 for paths to (1, 2) if the backtracking solution had explored NE first instead of last in the recursive explore
method:
start (0, 0) | +--- NE (1, 1) | | | +--- NE NE (2, 2) | | | +--- NE N (1, 2) - output | | | +--- NE E (2, 1) | +--- N (0, 1) | | | +--- N NE (1, 2) - output | | | +--- N N (0, 2) | | | | | +--- N N NE (1, 3) | | | | | +--- N N N (0, 3) | | | | | +--- N N E (1, 2) - output | | | +--- N E (1, 1) | | | +--- N E NE (2, 2) | | | +--- N E N (1, 2) - output | | | +--- N E E (2, 1) | +--- E (1, 0) | +--- E NE (2, 1) | +--- E N (1, 1) | | | +--- E N NE (2, 2) | | | +--- E N N (1, 2) - output | | | +--- E N E (2, 1) | +--- E E (2, 0)
If the solution had explored NE first instead of last, the solutions would have been printed in this order:
moves: NE N moves: N NE moves: N N E moves: N E N moves: E N N
There are 64 entries at the second level of the full tree. There are 512 entries at the third level of the full tree.
If our 8 Queens algorithm tried every possible square on the board for placing each queen, there would be (64*63*62*61*60*59*58*57) = 178,462,987,637,760 entries are there at the 8th and final level of the full tree. Our algorithm avoids such a huge number of choices by only placing one queen in each column of the board.
The 8 Queens explore
method stops once it finds one solution to the problem.
This is because the code has the following lines around its recursive call:
if (explore(b, col + 1)) { return true; }
The code could be modified so that it would find and output every solution to the problem by changing that code to the following:
explore(b, col + 1);
And changing the base case to the following:
if (col > b.size()) { System.out.println("One solution is as follows:"); b.print(); return true; }
You can perform a sequential search over the array using a loop, or you can sort the array using Arrays.sort
and then perform a binary search over it using Arrays.binarySearch
.
Closest value to the number of elements that the binary search algorithm will need to examineon an array of one million integers:
e. less than 1% (10,000 or fewer)
A sequential search must be used on an array of Point
objects because they do not implement Comparable
.
Arrays.binarySearch
and Collections.binarySearch
can be used successfully if the array or collection contains elements that are sorted, according to either their natural ordering or the ordering of a Comparator
.
Collections.sort
on a list of strings would arrange them in alphabetical order, case-sensitive.
To change the order, you could pass a Comparator
that defines a different order.
Collections.sort
would not work on a list of Point
objects by default because they do not implement the Comparable
interface.
To make it work, you could pass a Comparator
that defines an ordering for Point
s.
The AccountComparator
shown has a few errors:
Comparator
.
compare
, not compareTo
.
It should also accept two BankAccount
parameters, not just one.
this
.
double
s from a compare
method.
The result must be of type int
, and it must still work even if the accounts differ only by a few cents.
import java.util.*; public class AccountComparator extends Comparator<BankAccount> { public int compare(BankAccount account1, BankAccount account2) { if (!account1.getName().equals(account2.getName())) { return account1.getName().compareTo(account2.getName()); } else if (account1.getBalance() < account2.getBalance()) { return -1; } else if (account1.getBalance() > account2.getBalance()) { return 1; } else { return 0; } } }
We could easily reverse the order of our LengthComparator
by using the built-in method Collections.reverseOrder
, which accepts a Comparator
and returns a new one with the opposite order of the one passed in.
O(log N)
O(N)
O(N2)
O(N2)
O(N)
Complexity classes of the given algorithms in terms of N:
Complexity classes of the given statements:
The runtime complexity of both sequential searches is O(N).
Binary search requires a sorted dataset because it uses the ordering to jump to the next index. If the elements are out of order, the search isn't guaranteed to find the target element.
A binary search of 60 elements examines at most 6 elements, because log2 60 (when rounded up) equals 6.
The algorithm will examine indexes 4, 6, and 5 and will return -1. The algorithm doesn't work properly because the input array isn't sorted.
The binary search algorithm will examine the following indexes and return the following values for each search:
The binary search algorithm will examine the following indexes and return the following values for each search:
The parameter array type should be changed to double
.
Also, a new swap
method will be needed that accepts a double[]
as the first parameter.
Here's the new code:
public static void selectionSort(double[] a) { for (int i = 0; i < a.length - 1; i++) { // find index of smallest element int smallest = i; for (int j = i + 1; j < a.length; j++) { if (a[j] < a[smallest]) { smallest = j; } } swap(a, i, smallest); // swap smallest to front } }
After a single pass of the selection sort algorithm (a single swap), the state of the array is:
d.[-4, 17, 3, 94, 46, 8, 29, 12]
All steps of selection sort algorithm:
[29, 17, 3, 94, 46, 8, -4, 12] [-4, 17, 3, 94, 46, 8, 29, 12] [-4, 3, 17, 94, 46, 8, 29, 12] [-4, 3, 8, 94, 46, 17, 29, 12] [-4, 3, 8, 12, 46, 17, 29, 94] [-4, 3, 8, 12, 17, 46, 29, 94] [-4, 3, 8, 12, 17, 29, 46, 94]
[33, 14, 3, 95, 47, 9, -42, 13] [-42, 14, 3, 95, 47, 9, 33, 13] [-42, 3, 14, 95, 47, 9, 33, 13] [-42, 3, 9, 95, 47, 14, 33, 13] [-42, 3, 9, 13, 47, 14, 33, 95] [-42, 3, 9, 13, 14, 47, 33, 95] [-42, 3, 9, 13, 14, 33, 47, 95]
[7, 1, 6, 12, -3, 8, 4, 21, 2, 30, -1, 9] [-3, 1, 6, 12, 7, 8, 4, 21, 2, 30, -1, 9] [-3, -1, 6, 12, 7, 8, 4, 21, 2, 30, 1, 9] [-3, -1, 1, 12, 7, 8, 4, 21, 2, 30, 6, 9] [-3, -1, 1, 2, 7, 8, 4, 21, 12, 30, 6, 9] [-3, -1, 1, 2, 4, 8, 7, 21, 12, 30, 6, 9] [-3, -1, 1, 2, 4, 6, 7, 21, 12, 30, 8, 9] [-3, -1, 1, 2, 4, 6, 7, 21, 12, 30, 8, 9] [-3, -1, 1, 2, 4, 6, 7, 8, 12, 30, 21, 9] [-3, -1, 1, 2, 4, 6, 7, 8, 9, 30, 21, 12] [-3, -1, 1, 2, 4, 6, 7, 8, 9, 12, 21, 30]
[6, 7, 4, 8, 11, 1, 10, 3, 5, 9] [1, 7, 4, 8, 11, 6, 10, 3, 5, 9] [1, 3, 4, 8, 11, 6, 10, 7, 5, 9] [1, 3, 4, 8, 11, 6, 10, 7, 5, 9] [1, 3, 4, 5, 11, 6, 10, 7, 8, 9] [1, 3, 4, 5, 6, 11, 10, 7, 8, 9] [1, 3, 4, 5, 6, 7, 10, 11, 8, 9] [1, 3, 4, 5, 6, 7, 8, 11, 10, 9] [1, 3, 4, 5, 6, 7, 8, 9, 10, 11]
A merge sort of 32 elements will generate 63 total calls to mergeSort
and will perform the merge
operation 31 times.
State of the elements after five passes of the outermost loop of selection sort have occurred:
[1, 2, 3, 4, 5, 11, 9, 7, 8, 10]
Trace of merge sort algorithm:
[7, 2, 8, 4, 1, 11, 9, 5, 3, 10] [7, 2, 8, 4, 1], [11, 9, 5, 3, 10] [7, 2], [8, 4, 1], [11, 9], [5, 3, 10] [7], [2], [8], [4, 1], [11], [9], [5], [3, 10] [4], [1], [3], [10] [8], [1, 4], [5], [3, 10] [2, 7], [1, 4, 8], [9, 11], [3, 5, 10] [1, 2, 4, 7, 8], [3, 5, 9, 10, 11] [1, 2, 3, 4, 5, 7, 8, 9, 10, 11]
State of the elements after five passes of the outermost loop of selection sort have occurred:
[-3, -1, 1, 2, 4, 8, 7, 21, 12, 30, 6, 9]
Trace of merge sort algorithm:
[7, 1, 6, 12, -3, 8, 4, 21, 2, 30, -1, 9] [7, 1, 6, 12, -3, 8], [4, 21, 2, 30, -1, 9] [7, 1, 6], [12, -3, 8], [4, 21, 2], [30, -1, 9] [7], [1, 6], [12], [-3, 8], [4], [21, 2], [30], [-1, 9] [1], [6], [-3], [8], [21], [2], [-1], [9] [7], [1, 6], [12], [-3, 8], [4], [2, 21], [30], [-1, 9] [1, 6, 7], [-3, 8, 12], [2, 4, 21], [-1, 9, 30] [-3, 1, 6, 7, 8, 12], [-1, 2, 4, 9, 21, 30] [-3, -1, 1, 2, 4, 6, 7, 8, 9, 12, 21, 30]
The following statement about sorting and big-Oh is true:
b. Merge sort achieves an O(N log N) runtime by dividing the array in half at each step and then recursively sorting and merging the halves back together.Traces of merge sort algorithm:
[29, 17, 3, 94, 46, 8, -4, 12] [29, 17, 3, 94], [46, 8, -4, 12] [29, 17], [3, 94], [46, 8], [-4, 12] [29], [17], [3], [94], [46], [8], [-4], [12] [17, 29], [3, 94], [8, 46], [-4, 12] [3, 17, 29, 94], [-4, 8, 12, 46] [-4, 3, 8, 12, 17, 29, 46, 94]
[6, 5, 3, 7, 1, 8, 4, 2] [6, 5, 3, 7], [1, 8, 4, 2] [6, 5], [3, 7], [1, 8], [4, 2] [6], [5], [3], [7], [1], [8], [4], [2] [5, 6], [3, 7], [1, 8], [2, 4] [3, 5, 6, 7], [1, 2, 4, 8] [1, 2, 3, 4, 5, 6, 7, 8]
[33, 14, 3, 95, 47, 9, -42, 13] [33, 14, 3, 95], [47, 9, -42, 13] [33, 14], [3, 95], [47, 9], [-42, 13] [33], [14], [3], [95], [47], [9], [-42], [13] [14, 33], [3, 95], [9, 47], [-42, 13] [14, 33], [3, 95], [9, 47], [-42, 13] [3, 14, 33, 95], [-42, 9, 13, 47] [-42, 3, 9, 13, 14, 33, 47, 95]
Statement that is true about stacks and queues:
c. A queue's remove method removes and returns the element at the front of the queue.A real-world example of data that could be modeled using a stack is the plates in a cafeteria, or the undo/redo feature of a software application. A real-world example of a queue is the waiting line at a fast-food restaurant.
When you push onto a stack, the new element is added to the top. When you pop from a stack, the top element is removed and returned.
When you add to a queue, the new element is added to the back. When you remove from a queue, the front element is removed and returned.
The value 3 will be returned from the stack.
The value 1 will be returned from the queue.
The problem with the code is that Queue
is an interface, so it cannot be instantiated.
Instead, construct a LinkedList
object:
Queue<Integer> q = new LinkedList<Integer>();
Stack<String> stack = new Stack<String>(); stack.push("hello"); stack.push("hi"); stack.push("goodbye"); stack.push("howdy"); System.out.println(stack);
Stack<Integer> stack = new Stack<Integer>(); for (int i = 100; i >= 0; i -= 2) { stack.push(i); } System.out.println(stack);
Queue<String> queue = new LinkedList<String>(); queue.add("alpha"); queue.add("beta"); queue.add("gamma"); queue.add("delta"); System.out.println(queue);
To access elements in the middle of a stack or queue, you must remove/pop out elements until you reach the one you're looking for. In many cases, it's important to save the removed elements somewhere so that you can put them back into the stack or queue when you are done.
Stacks and queues are still useful despite their limited functionality because they are simple and easy to use, and because their operations are all efficient to execute. Many common operations are also naturally represented as a stack or queue.
The code produces the following output:
[you, are, how]
10 7 5 false 2 3 8 3
The code produces the following output:
2 10 10 4 6 6 3
Output produced by the mystery1
method by each call:
[1, 1, 6, 6, 2, 2]
[9, 9, 15, 15, 4, 4, -3, -3, 42, 42]
[40, 40, 50, 50, 60, 60, 10, 10, 20, 20, 30, 30]
Output produced by the mystery2
method by each call:
[1, 3, 5] [2, 4, 6]
[-3, 15, 9, 71] [42, 4]
[3] [30, 20, 10, 60, 50, 40, 0]
Output produced by the mystery3
method by each call:
[-1, -3, -5]
[-42, -4, -71]
[-10, -60, -50]
The problem with the code is that the size of the queue is changing while the loop goes over it. Another problem with the code is that it destroys the contents of the queue being examined. The following version of the code fixes both problems:
int sum = 0; Queue<Integer> backup = new LinkedList<Integer>(); int largest = q.remove(); int size = q.size(); for (int i = 0; i < size; i++) { int n = q.remove(); largest = Math.max(largest, n); backup.add(n); } while (!backup.isEmpty()) { q.add(backup.remove()); }
The problem with the code is that it calls the remove
method twice on each element, which double-removes it and therefore skips elements.
Another problem with the code is that it destroys the contents of the stack being examined.
The following version of the code fixes both problems:
int sum = 0; Queue<Integer> backup = new LinkedList<Integer>(); while (!q.isEmpty()) { int n = q.remove(); if (n > 0) { sum += n; } backup.add(n); } while (!backup.isEmpty()) { q.add(backup.remove()); }
The problem with the code is that it puts the odd elements back at the top of the stack each time, so it will never make progress down the stack toward the bottom. If the stack contains any odd elements, the code will get stuck in an infinite loop. The following version of the code fixes the problem:
Stack<Integer> backup = new Stack<Integer>(); while (!s.isEmpty()) { int n = s.pop(); if (n % 2 != 0) { backup.push(n); } } while (!backup.isEmpty()) { s.push(backup.pop()); }
public static void print(Queue<Integer> queue) { for (int i = 0; i < queue.size(); i++) { int n = queue.remove(); System.out.println(n); queue.add(n); } }
public static void printLongest(Stack<String> stack) { Stack<String> backup = new Stack<String>(); String longest = stack.pop(); backup.push(longest); while (!stack.isEmpty()) { String s = stack.pop(); if (s.length() > longest.length()) { longest = s; } backup.push(s); } while (!backup.isEmpty()) { // restore stack stack.push(backup.pop()); } System.out.println(longest); }
An array list's size is the number of elements that have been added to it. Its capacity is the length of its internal array. The size is always less than or equal to the capacity.
The ArrayIntList class keeps at least two fields: an array of elements and a size. The array is necessary because it is where we store the data inside the collection. The size is necessary because some of the elements at the end of the array may not be meaningful values. If we removed the size field, we would not know how many elements were meaningful.
If the fields were static, all lists would share the same array and size. Any modification to one list would also be seen in all other lists. The client's output would be the following:
[1, 82, 97, 7, -8] [1, 82, 97, 7, -8]
In this section's version of the list class, if the client tries to add too many values, the code crashes with an out of bounds exception.
We use a toString
method because this is the standard way of printing objects in Java.
It is also more versatile than a print
method because it can print the text representation of the list to any target, such as a file or GUI.
Having accessor methods such as size
is better than making the fields public because it preserves the encapsulation of the object.
As discussed in Chapter 8, this improves the cleanliness of the abstraction of the object and would allow us to change the implementation later if so desired.
It is most expensive to insert or remove at the beginning of the list, because all elements must be shifted to the right by one index.
public int min() { if (size == 0) { throw new IllegalStateException(); } int minValue = elementData[0]; for (int i = 1; i < size; i++) { minValue = Math.min(minValue, elementData[i]); } return minValue; } public int max() { if (size == 0) { throw new IllegalStateException(); } int maxValue = elementData[0]; for (int i = 1; i < size; i++) { maxValue = Math.max(maxValue, elementData[i]); } return maxValue; }
The preconditions are that the client will not try to construct a list with a negative capacity, and that the client will never pass an index that is negative or outside the size of the list.
The checkIndex
method tests whether a given index is between 0 and the size of the list, and if not, throws an exception.
If the client passes an invalid index by mistake, the method will halt the program's execution.
The checkCapacity
method tests whether the array's size will exceed the length of the internal array (capacity), and if so, throws an exception.
If the client adds too many elements to the list, the method will halt the program's execution.
With our preconditions, we may now assume that size <= capacity
at all times.
We can also assume all index parameters passed to various methods are valid once they get through the checkIndex
test.
These methods are provided as a convenience to the client, to give the list object a more simple and pleasant external interface to use.
The list doubles in size when it exceeds its capacity. This is done instead of resizing by a constant amount so that the overall cost of adding elements to the end of a list will be amortized to be O(1), constant time.
An iterator provides a standard way of examining the elements of a collection.
The iterator keeps track of the list to examine, the current index in the list, and whether it is safe to remove an element from the list using the iterator.
The iterator knows there are more elements to examine if its current index is below the size of the list. If there is no such element but the client calls next, an exception is thrown.
The precondition of remove
is that the method next
has been called and that next
was called more recently than any other call to remove
.
The precondition is enforced by a boolean
flag in the iterator whose value is changed on every call to next
or remove
.
If the precondition is violated, an exception is thrown.
public int sum() { int total = 0; for (int i = 0; i < size; i++) { total += elementData[i]; } return total; }
public double average() { if (isEmpty()) { return 0.0; } else { return (double) sum() / size; } }
Java does not allow the construction of arrays of generic types.
To work around this, we instead create an array of Object[]
and cast it to type E[]
.
Instead of 0, we fill all of our empty cells of type E
with null
.
We must modify indexOf
to compare objects using equals
rather than ==
because ==
compares only references and not the state of the objects.
An annotation is a special directive to the compiler with additional information about a class, method, or other structure. Annotations help us when writing our generic list because we can instruct the compiler not to warn us about potentially unsafe casting operations.
It is important to set the removed/cleared elements to null
so that Java's garbage collector can potentially reclaim their memory.
When the iterator is an inner class, it can directly access the fields of the enclosing list object. This makes it easier for the iterator to do its work without keeping track of as much state.
The difference between a linked list and an array list is that while an array list stores all of its elements in a single large array, a linked list stores each element inside its own container object called a node. The nodes are connected (linked) to each other by references. The two kinds of lists are similar in that they both implement the same external operations to clients, such as methods for adding, removing, accessing, and locating elements.
A node is a small object that stores a single element of a linked list. The list object stores reference(s) to a small number of nodes, perhaps only the front of the list. The front contains a chain of references that connect to the other elements of the list.
The next
field of the last node of a list, as well as any unspecified next
field, stores null
.
When the client tries to go past the end of a linked list, there will be a null pointer exception.
+----+----+ +----+----+ list ----> | 1 | +----> | 3 | / | +----+----+ +----+----+
+----+----+ +----+----+ +----+----+ list ----> | 1 | +----> | 3 | +----> | 2 | / | +----+----+ +----+----+ +----+----+
+----+----+ +----+----+ list ----> | 4 | +----> | 3 | / | +----+----+ +----+----+
+----+----+ +----+----+ list ----> | 1 | +----> | 2 | / | +----+----+ +----+----+
list.next.next = new ListNode(3, null); // 2 -> 3
list = new ListNode(3, list); // 3 -> 1 and list -> 3
temp.next.next = list.next; // 4 -> 2 list.next = temp; // 1 -> 3
ListNode list2 = list; // list2 -> 1 list = list.next; // list -> 2 list2.next = list2.next.next; // 1 -> 3 list.next = null; // 2 /
ListNode temp = list; // temp -> 5 list = list.next; // list -> 4 temp.next = list.next; // 5 -> 3 list.next = temp; // 4 -> 5
ListNode temp = list.next.next; // temp -> 3 temp.next = list.next; // 3 -> 4 list.next.next = list; // 4 -> 5 list.next.next.next = null; // 5 / list = temp; // list -> 3
list.next.next.next = temp; // 3 -> 4 temp.next.next = list.next.next; // 5 -> 3 list.next.next = null; // 2 / ListNode temp2 = temp.next; // temp2 -> 5 temp.next = list.next; // 4 -> 2 list = temp2; // list -> 5
list2.next.next.next = list; // 4 -> 1 list.next = list2; // 1 -> 2 list = list2.next.next; // list -> 4 list2 = list2.next; // list2 -> 3 list2.next = null; // 3 / list.next.next.next = null; // 2 /
ListNode list2 = list.next.next; // list2 -> 3 list.next.next.next.next = list.next; // 4 -> 2 ListNode temp = list; // temp -> 1 list = list.next.next.next; // list -> 4 list2.next = temp; // 3 -> 1 list.next.next = null; // 2 / list2.next.next = null; // 1 /
The two ways to change an object of our linked list class are to change its front reference or to change the next or data field of an existing node.
Inserting and removing is most expensive at the end of the list, because the code must loop through all of the next references to reach the end. This is the opposite of the array list, which inserts/removes most slowly at the start because of the shifting of elements that is required.
The loop should stop and index i - 1
, the index before the one to add or remove.
This is so that you can adjust the preceding node's next reference.
Resizing is not necessary for a linked list, since more nodes can be dynamically allocated. The only limit on the number of elements is the amount of memory available to the Java virtual machine.
ListNode current = list; while (current != null) { current.data = 42; current = current.next; }
ListNode current = list; while (current.next != null) { current = current.next; } current.data = 42;
ListNode current = list; while (current.next != null) { current = current.next; } current.next = new ListNode(42);
public int min() { if (front == null) { throw new IllegalStateException(); } else { int minValue = front.data; ListNode current = front.next; while (current != null) { minValue = Math.min(minValue, current.data); current = current.next; } return minValue; } } public int max() { if (front == null) { throw new IllegalStateException(); } else { int maxValue = front.data; ListNode current = front.next; while (current != null) { maxValue = Math.max(maxValue, current.data); current = current.next; } return maxValue; } }
The four cases examined by the addSorted
method are: the typical case in the "middle" of the list; inserting at the back of the list; inserting at the front; and the empty list case.
The "inchworm approach" is when an algorithm keeps track of two linked node references, one for the previous node and one for the current node.
They are moved forward together over the list until a particular position is reached.
At that point, the previous reference is modified as appropriate.
One advantage of this approach is that you do not need to write complex chains of dereferences such as current.next.data
.
public int sum() { int total = 0; ListNode current = front; while (current != null) { total += current.data; current = current.next; } return total; } public double average() { if (front == null) { return 0.0; } else { int total = 0; int count = 0; ListNode current = front; while (current != null) { total += current.data; current = current.next; count++; } return (double) total / count; } }
The main advantage of the IntList
interface is that client code can take advantage of polymorphism.
A client program can deal with an IntList
reference and the actual object at runtime can be an instance of either kind of list.
public void firstLast(IntList list) { if (!list.isEmpty()) { int element = list.get(0); list.remove(0); list.add(element); } }
When changing the linked list to store elements of type E
, the list class, its nested classes, and several methods must be changed to use the new generic type.
We must change any comparisons between objects to use equals
instead of ==
.
In our code, we also use dummy header nodes and add a back
reference to increase the efficiency when adding to the end of the list.
Iterators are important with linked lists because it is inefficient to traverse a linked list by calling the get
method once for each index.
Such an algorithm must repeatedly traverse the entire list to each index passed.
The iterator instead remembers its position between calls to next
.
The linked list iterator keeps a reference to its current node and a boolean
for whether it is safe to remove an element.
The iterator knows there are more elements by looking at the next
reference of its current node.
A tree has only one root. A tree could have more leaves than branches (for example, a perfect tree of height 3) or could have more branches than leaves (for example, a tree whose root has two child nodes, each of which has one child, each of which has one child).
Twice as many leaves as branches:
+---+ | 1 | +---+ / \ / \ +---+ +---+ | 2 | | 3 | +---+ +---+
Same number of leaves as branches:
+---+ | 1 | +---+ / \ / \ +---+ +---+ | 2 | | 3 | +---+ +---+ / / \ / / \ +---+ +---+ +---+ | 4 | | 5 | | 6 | +---+ +---+ +---+
If we removed the root != null
test from the printPreorder
method, the method would eventually crash when trying to dereference root
to examine its data or to make a recursive call.
public void printPostorder() { System.out.print("postorder:"); printPostorder(overallRoot); System.out.println(); } private void printPostorder(IntTreeNode root) { if (root != null) { printPostorder(root.left); printPostorder(root.right); System.out.print(" " + root.data); } }
public void printMirror() { System.out.print("mirror:"); printMirror(overallRoot); System.out.println(); } private void printMirror(IntTreeNode root) { if (root != null) { printMirror(root.right); System.out.print(" " + root.data); printMirror(root.left); } }
Many tree methods use a public/private pair because the algorithms are best implemented recursively, but each recursive call must examine a progressively smaller portion of the tree. Therefore the header of the private method generally accepts a tree node as an additional parameter.
public int size() { return size(overallRoot); } private int size(IntTreeNode root) { if (root == null) { return 0; } else { return 1 + size(root.left) + size(root.right); } }
public int min() { if (overallRoot == null) { throw new IllegalStateException("empty tree"); } return min(overallRoot); } private int min(IntTreeNode root) { if (root.left == null && root.right == null) { return root.data; } else { int minValue = root.data; if (root.left != null) { minValue = Math.min(minValue, min(root.left)); } if (root.right != null) { minValue = Math.min(minValue, min(root.right)); } return minValue; } }
// max is written in the same fashion as min, but with 'max' // in place of 'min' everywhere in the code. public int max() { if (overallRoot == null) { throw new IllegalStateException("empty tree"); } return max(overallRoot); } private int max(IntTreeNode root) { if (root.left == null && root.right == null) { return root.data; } else { int maxValue = root.data; if (root.left != null) { maxValue = Math.max(maxValue, max(root.left)); } if (root.right != null) { maxValue = Math.max(maxValue, max(root.right)); } return maxValue; } }
public int countBranches() { return countBranches(overallRoot); } private int countBranches(IntTreeNode root) { if (root == null || (root.left == null && root.right == null)) { return 0; } else { return 1 + countBranches(root.left) + countBranches(root.right); } }
A binary search tree is one that is ordered such that smaller nodes appear to the left and larger nodes appear to the right.
Valid binary search trees: (b), if duplicates are allowed; (c); and (e).
An in-order traversal of a BST will examine the elements in their sorted order. For example, in-order traversal of a BST of integers will visit the integers in increasing numerical order.
Resulting tree:
+--------+ | Leia | +--------+ / \ / \ +--------+ +--------+ | Boba | | R2D2 | +--------+ +--------+ \ / \ / +--------+ +--------+ | Darth | | Luke | +--------+ +--------+ / \ / \ +--------+ +--------+ | Chewy | | Han | +--------+ +--------+ \ \ +--------+ | Jabba | +--------+
Resulting tree:
+-----+ | Meg | +-----+ / \ / \ +-----+ +--------+ | Joe | | Stewie | +-----+ +--------+ / \ / / \ / +-------+ +------+ +-------+ | Brian | | Lois | | Peter | +-------+ +------+ +-------+ \ \ \ \ +-----------+ +----------+ | Cleveland | | Quagmire | +-----------+ +----------+
Resulting tree:
+------------+ | Kirk | +------------+ / \ / \ / \ +------------+ +------------+ | Chekov | | Spock | +------------+ +------------+ \ / \ \ / \ \ / \ +------------+ +------------+ +------------+ | Khaaaan! | | Scotty | | Uhuru | +------------+ +------------+ +------------+ / / / / / / +------------+ +------------+ | McCoy | | Sulu | +------------+ +------------+
Resulting tree:
+------------+ | Lisa | +------------+ / \ / \ / \ +------------+ +------------+ | Bart | | Marge | +------------+ +------------+ \ / \ \ / \ \ / \ +------------+ +------------+ +------------+ | Homer | | Maggie | | Smithers | +------------+ +------------+ +------------+ / / / / / / +------------+ +------------+ | Flanders | | Milhouse | +------------+ +------------+
The add
method needs to return the newly added node to enable the x = change(x)
pattern.
If no reference to the new node is returned, it is not possible to attach that new node to the rest of the tree.
Such attachment is crucial to the working of the algorithm.
The x = change(x)
pattern is an algorithmic strategy where a recursive method (such as a binary tree method) will accept a node's initial state as a parameter and will then return the node's new state as its result.
This returned result will be stored by the client back into its original place of reference.
This allows recursive methods to return modified versions of trees using elegant code that follows the "zen" of recursion.
The contains
method is efficient on BSTs and needs to go at most one direction in each recursive call.
Each call advances one level in the tree, so the total number of calls / advancements is the height of the tree, or N.
This is logarithmic with respect to the total number of nodes in the tree (its size).
This second version of contains
is much less efficient than the one written in the chapter.
It goes both to the left and to the right recursively to find the value, but this does not take advantage of the sortedness of the tree.
It will have linear O(N) runtime rather than the much faster O(log N) desired runtime of our original method.
public int min() { if (overallRoot == null) { throw new IllegalStateException("empty tree"); } return min(overallRoot); } private int min(IntTreeNode root) { if (root.left == null) { return root.data; } else { return min(root.left); } } public int max() { if (overallRoot == null) { throw new IllegalStateException("empty tree"); } return max(overallRoot); } private int max(IntTreeNode root) { if (root.right == null) { return root.data; } else { return max(root.right); } }
When converting the tree to store type E
, we must add a type parameter to the class header.
This parameter must be Comparable
.
When examining whether a given element is too small or large (whether to go left or right recursively) in methods such as add
or contains
, we must call compareTo
instead of using the <
and >
operators that do not work with objects.
To add a tree iterator, each node would need to have a reference to the "next" node after it, so that the nodes could be traversed in a left-to-right order. Another solution would be for nodes to have references to their parents so that the iterator could go back up the tree as necessary when traversing through the elements.
Hashing is a process of mapping element values to integer indexes and storing the elements at those indexes in an array. Hashing is a good way of implementing a set because it provides theoretically O(1) runtime for adding, removing, and searching a set.
The following statement about hash tables is true:
d. A hash function maps element values to integer indexes in the hash table.A hash table being "full" depends on what strategy is used to resolve collisions. If probing is used, the table is literally full when it has no empty buckets for storing elements, but often it is considered to be too full when its load factor (the ratio of its size to its array capacity) reaches some maximum like 0.75 or 0.66. A hash table that uses separate chaining is never literally full because elements can be added indefinitely to each bucket's linked list, but it still resizes once the load factor reaches some threshold.
The code shown is incorrect because it just copies over every element from the hash table into the same index in the new larger array. This may lead to elements being at the wrong index, because the proper index is based on the element's hash code modded by the array length. For example, the value 37 would be at index 7 in an array of size 10, but in a larger array of size 20 it should be at index 17.
After adding the elements, the hash table's state is the following:
0 1 2 3 4 5 6 7 8 9 [ 80, 0, 2, 42, 0, 35, 15, 95, 66, 0]
After adding the elements, the hash table's state is the following:
0 1 2 3 4 5 6 7 8 9 [ |, /, |, /, /, |, |, /, /, /] 80 42 95 66 | | 2 15 | 35
After adding the elements, the hash table's state is the following:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [ 0, 0, 32, 0, 24, 5, 44, 47, 0, 0, 0, 0, 0, X, 0, X, 0, 17, 0, 0] size = 6 capacity = 20 load factor = 0.3
After adding the elements, the hash table's state is the following:
0 1 2 3 4 5 6 7 8 9 10 [ 0, 0, 0, 0, 70, 82, 50, 39, 15, 29, 18] size = 7 capacity = 11 load factor = 0.636
hashCode
implementation, according to the contract.
It distributes hash codes somewhat well, but it has certain groups of points that will collide with each other unnecessarily (for example, every point with an x-coordinate or y-coordinate of 0 will have the same hash code).
hashCode
implementation, according to the contract.
But it distributes hash codes extremely poorly; literally it could not be worse in that respect, because every point has the same hash code.
It's important to note that it is still a technically correct implementation, though it works very poorly in terms of hash table performance.
hashCode
implementation, according to the contract.
It does not consistently return the same hash code for the same point object state.
hashCode
method for a Date
class (the constant multipliers for each component are somewhat arbitrary):
public int hashCode() { return 1331 * year + 37 * month + day; }
hashCode
method for a Student
class (the constant multipliers for each component are somewhat arbitrary):
public int hashCode() { return 373 * name.hashCode() + 1341 * studentID + 31 * Double.valueOf(weight).hashCode(); }
After adding the key/value pairs, the hash table's state is the following:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [ /, /, /, /, X, /, 6=Tina, 7=Meghan, /, /, /, /, /, 33=Kona, 34=Tyler, 15=Daisy, /, X, /, /] size = 5 capacity = 20 load factor = 0.4
The following statement about min-heaps is true:
b. The smallest value is the root.If a binary heap has 26 nodes, its height is 5. If it has 73 nodes, its height is 7. We know for sure because every heap is a complete tree, so its shape and height are predictable given its size. The height of a heap of size N will always be equal to ceil(log2 N).
For our heap implementation, an element at index 8 of the array has its children at indexes 16 and 17. Its parent is at index 4. An element at index 23 has its children at indexes 46 and 47, and its parent at index 11.
The min-heap after 21 is added:
12 / \ 29 21 / \ / \ 30 39 72 91 / \ / \ / 55 64 40 99 84
The min-heap after 7 is added:
7 / \ 29 12 / \ / \ 30 39 21 91 / \ / \ / \ 55 64 40 99 84 72
The resulting binary min-heap after all adds is the following:
2 / \ 3 4 / \ / \ 6 7 5 8 / 9
The resulting binary min-heap after each of the three removals is the following. After first removal:
3 / \ 6 4 / \ / \ 9 7 5 8
After second removal:
4 / \ 6 5 / \ / 9 7 8
After third removal:
5 / \ 6 8 / \ 9 7
The resulting binary min-heap after all adds is the following:
1 / \ 3 7 / \ / \ 8 11 15 12 / \ 14 9
The resulting binary min-heap after each of the three removals is the following. After first removal:
3 / \ 8 7 / \ / \ 9 11 15 12 / 14
After second removal:
7 / \ 8 12 / \ / \ 9 11 15 14
After third removal:
8 / \ 9 12 / \ / 14 11 15
The array does not begin with its root at 1. (The array also contains some incorrect element values, but that's an error on the part of the authors. Oh well.) The proper array state is the following:
0 1 2 3 4 5 6 7 8 9 10 11 12 [ /, 12, 29, 70, 30, 39, 84, 91, 55, 64, 40, 99, ...]
Heap represented by the given array:
29 / \ 41 30 / \ / \ 55 68 37 41 / 80
Array representation of the heap from Self-Check #19:
0 1 2 3 4 5 6 7 8 9 [ /, 2, 3, 4, 6, 7, 5, 8, 9, ...]
Array representation of the heap from Self-Check #21:
0 1 2 3 4 5 6 7 8 9 10 [ /, 1, 3, 7, 8, 11, 15, 12, 14, 9, ...]
Because functional programming focuses so much on individual functions, the community of programmers who use functional programming regularly have concluded that side effects should be avoided when possible.
Calling System.out.println
is considered a side effect because it produces a noticeable outcome, namely printing output to the console.
So you can detect whether a given function was called or not by looking for output.
This doesn't mean that printing output is a bad thing, merely that it is a side effect.
The function's side effect is that it modifies the array that was passed in. It could be changed to remove side effects by having it return the new array state rather than changing the existing array.
// Returns a new array whose values are twice as large as // the values of all elements in the given array. public static int[] doubleAll(int[] a) { int[] result = new int[a.length]; for (int i = 0; i < a.length; i++) { result[i] = 2 * a[i]; } return result; }
Rewritten version of the program with no side effects:
public class SideEffect2 { public static int f(int x, int n) { x = x * 2; return x + n; } public static void main(String[] args) { int x = 5; int result = f(x, 5) + f(x, 5); System.out.println(result); } }
To support subtraction problems, we must call giveProblems
and pass a lambda function that does subtraction, such as:
giveProblems(console, 3, "-", (x, y) -> x * y);
(x) -> x * x
(a, b) -> (a > b ? a : b)
(first, last) -> (last + ", " + first)
A stream is not the same as an array. Both can be thought of as containing a collection of elements. But streams do not support mutating data, and you can only access an element at a time, not random access like in an array.
The variable result
stores 4.
int sum = Arrays.stream(a) .map(n -> -n) .sum();
int evens = (int) Arrays.stream(a) .filter(n -> n % 2 == 0) .count();
The variable result
stores 44.
The code does not compile because it returns an optional result.
You must call getAsDouble
to retrieve the actual double value:
double avg = DoubleStream.of(3.1, -4.5, 8.9, -6.2, 1.0) .map(n -> Math.abs(n)) .average() .getAsDouble();
A bound variable is inside the lambda, typically one of its parameters. A free variable is a variable referred to in the lambda's code that is declared outside the lambda and enclosed into its closure.
The free variables are a
and b
, and the bound variables are c
and d
.
The code is not allowed to modify the free variable a
.
The code could be modified as follows:
int a = 10; int b = 20; int sum = IntStream.of(1, 2, 3, 4, 5) .map(n -> n + b - (a + 1)) .sum();
The code produces the following output:
10 28 33 28 49 56 49
// this version will not print duplicate values Arrays.stream(a) .map(Math::abs) .distinct() .forEach(System.out::println);
// retain the positive integers only int[] positives = Arrays.stream(a) .filter(n -> n > 0) .toArray();
// print all four-letter words in the list: list.stream() .filter(s -> s.length() == 4) .forEach(System.out::println);
// print all lines from notes.txt that are at least 40 chars long Files.lines(Paths.get("notes.txt")) .filter(line -> line.length() >= 40) .forEach(System.out::println);
The code has four problems:
(1) The Files.lines
method accepts a path, not a string;
(2) The map
call should be mapToInt
;
(3) The max
call needs to be followed by a call to getAsInt
because it returns an optional integer result; and
(4) The overall method must have throws IOException
in its header.
The corrected code is:
// Returns the length of the longest line in the given file. // Assumes that the file contains at least one line. public static int longestLineLength(String filename) throws IOException { return Files.lines(Paths.get(filename)) .mapToInt(String::length) .max() .getAsInt(); }