diff --git a/.gitignore b/.gitignore
index 7f8865a..7e1aadf 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,9 @@
# IntelliJ project files
-solutions/.idea
+.idea
+solutions/solutions.iml
+
+# Generated files
+out
# Temporary files
*~
diff --git a/README.md b/README.md
index 0834a57..667bcf2 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Programming 2: Ally's Tutorial Questions
+# Java Programming: Ally's Tutorial Questions
The best way to learn a programming language and the concepts that underlie the language's design is to do *lots* of programming.
@@ -43,7 +43,7 @@ To help you decide how you should prioritise working through the questions, I ha
* Recap: focuses on recapping basic imperative concepts of Java: loops, recursion, variables, arrays and enumerations.
* SimpleObjects: covers basic use of objects (with little or no use of interfaces, inheritance, etc.).
* Interfaces: covers concepts relating to Java interfaces.
-* Streams: focuses on functional programming features of Java: streams, lambdas and method references.
+* Functional: focuses on functional programming features of Java: streams, lambdas and method references.
* Inheritance: covers the design and implementation of subclasses.
* AbstractClasses: focuses on using abstract superclasses to share common state and methods among subclasses.
* Generics: focuses on building generic containers, and on technical aspects of Java generics.
@@ -63,8 +63,6 @@ As a result, you will likely find that some of your answers differ from the samp
I'm very happy to discuss alternative solutions. Also, please get in touch if there are parts of the solutions that you do not understand, of if you spot errors.
-TODO: some additional questions on streams are coming soon!
-
| Label | Name | Topic tag(s) | Depends on | Solution |
|---------------------------|-----------------------------------|-------------------------|-------------|----------|
| [98e3](questions/98e3.md) | *... 1 4 2 1 4 2 1 ...* | Recap | | [Solution](solutions/98e3.md) |
@@ -85,11 +83,14 @@ TODO: some additional questions on streams are coming soon!
| [0378](questions/0378.md) | *Comparing people* | Interfaces | | [Solution](solutions/0378.md) |
| [6346](questions/6346.md) | *Depth of arithmetic expressions* | Interfaces | | [Solution](solutions/6346.md) |
| [e6fd](questions/e6fd.md) | *Bit sets* | Interfaces | | [Solution](solutions/e6fd.md) |
+| [fe94](questions/fe94.md) | *Using Stream.map and Stream.filter* | Functional | | [Solution](solutions/fe94.md) |
+| [68e6](questions/68e6.md) | *Using Stream.reduce* | Functional | | [Solution](solutions/68e6.md) |
| [0f05](questions/0f05.md) | *Coloured points* | Inheritance | | [Solution](solutions/0f05.md) |
| [dd4c](questions/dd4c.md) | *Clocks* | Inheritance | | [Solution](solutions/dd4c.md) |
| [8f65](questions/8f65.md) | *Lucky battling fighters with inheritance* | Inheritance | [8d24](questions/8d24.md) | [Solution](solutions/8f65.md) |
| [845d](questions/845d.md) | *Books and dictionaries* | Inheritance | | [Solution](solutions/845d.md) |
| [e93f](questions/e93f.md) | *Apparent and actual types* | Inheritance | | [Solution](solutions/e93f.md) |
+| [d3f5](questions/d3f5.md) | *Streams and downcasting* | Functional, Inheritance, Generics | | [Solution](solutions/d3f5.md) |
| [5235](questions/5235.md) | *Equality between points* | ObjectEquality | [0f05](questions/0f05.md) | [Solution](solutions/5235.md) |
| [710c](questions/710c.md) | *The consequences of overriding `equals`* | ObjectEquality | [5235](questions/5235.md) | [Solution](solutions/710c.md) |
| [aa68](questions/aa68.md) | *Symmetric equality testing* | ObjectEqualilty | [5235](questions/5235.md) | [Solution](solutions/aa68.md) |
@@ -103,8 +104,12 @@ TODO: some additional questions on streams are coming soon!
| [a6e7](questions/a6e7.md) | *Int set iterators* | AbstractClasses, Interfaces | [8a61](questions/8a61.md) [85bb](questions/85bb.md) | [Solution](solutions/a6e7.md) |
| [2ffc](questions/2ffc.md) | *Generic stacks* | Generics | [1486](questions/1486.md) | [Solution](solutions/2ffc.md) |
| [b401](questions/b401.md) | *Generic sets* | Generics | [8a61](questions/8a61.md) | [Solution](solutions/b401.md) |
+| [336b](questions/336b.md) | *Evolving the Set interface* | Interfaces, Advanced | [b401](questions/b401.md) | [Solution](solutions/336b.md) |
+| [17b1](questions/17b1.md) | *Default methods* | Interfaces, Advanced | | [Solution](solutions/17b1.md) |
| [96df](questions/96df.md) | *Tree nodes* | Generics | | [Solution](solutions/96df.md) |
| [7041](questions/7041.md) | *Cloning tree nodes* | Generics | [96df](questions/96df.md) | [Solution](solutions/7041.md) |
+| [888a](questions/888a.md) | *Generic methods with streams* | Generics, Functional | [68e6](questions/68e6.md) | [Solution](solutions/888a.md) |
+| [11e2](questions/11e2.md) | *Bounded generic methods with streams* | Generics, Functional | | [Solution](solutions/11e2.md) |
| [c822](questions/c822.md) | *Problems cloning tree nodes* | Advanced | [7041](questions/7041.md) | [Solution](solutions/c822.md) |
| [735a](questions/735a.md) | *Generic iterators* | Generics | [85bb](questions/85bb.md) [a6e7](questions/a6e7.md) [b401](questions/b401.md) | [Solution](solutions/735a.md) |
| [876b](questions/876b.md) | *Generics and subclasses* | Generics, Inheritance | | [Solution](solutions/876b.md) |
@@ -112,7 +117,7 @@ TODO: some additional questions on streams are coming soon!
| [b4a5](questions/b4a5.md) | *Observing the garbage collector* | MemoryManagement | | [Solution](solutions/b4a5.md) |
| [1ae9](questions/1ae9.md) | *Reusing immutable value objects* | MemoryManagement | [0f05](questions/0f05.md) | [Solution](solutions/1ae9.md) |
| [290b](questions/290b.md) | *Memory leaks in Java* | MemoryManagement, Advanced | | [Solution](solutions/290b.md) |
-| [5566](questions/5566.md) | *Exception-throwing stacks* | Exceptions | [question 1486](1486.md) | [Solution](solutions/5566.md) |
+| [5566](questions/5566.md) | *Exception-throwing stacks* | Exceptions | [1486](1486.md) | [Solution](solutions/5566.md) |
| [a22c](questions/a22c.md) | *No duplicate email addresses* | Exceptions | [dc38](questions/dc38.md) | [Solution](solutions/a22c.md) |
| [e093](questions/e093.md) | *Average of numbers* | Exceptions | | [Solution](solutions/e093.md) |
| [7e2a](questions/7e2a.md) | *Stack overflow* | Exceptions | | [Solution](solutions/7e2a.md) |
@@ -124,7 +129,8 @@ TODO: some additional questions on streams are coming soon!
| [1171](questions/1171.md) | *Cloning graphs* | Advanced | | [Solution](solutions/1171.md) |
| [f763](questions/f763.md) | *Simulating garbage collection* | Advanced | | [Solution](solutions/f763.md) |
| [9a9b](questions/9a9b.md) | *Transposing tunes* | Advanced | | [Solution](solutions/9a9b.md) |
+| [b33f](questions/b33f.md) | *Logging using a functional interface* | Advanced | [888a](questions/888a.md) | [Solution](solutions/b33f.md) |
## More hex strings
-When I am gone, if someone wants to add more questions then please consume the remaining [hex strings here](questions/hex_strings.md).
+When I am gone, if someone wants to add more questions then please consume the remaining [hex strings here](questions/hex_strings.md).
diff --git a/google_checks_edited.xml b/google_checks_edited.xml
new file mode 100755
index 0000000..6c46a05
--- /dev/null
+++ b/google_checks_edited.xml
@@ -0,0 +1,335 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/questions/11e2.md b/questions/11e2.md
new file mode 100755
index 0000000..02b7324
--- /dev/null
+++ b/questions/11e2.md
@@ -0,0 +1,38 @@
+[Back to questions](../README.md)
+
+## 11e2: *Bounded generic methods with streams*
+
+In a class, `Example`, write a static method, `getSmallestCollection`. For any element type `E` this method should accept a `List`, `collections`, of any type `C` such that `C` is a subtype of `Collection` (including `Collection` itself).
+
+The return type of the method should be `Optional`. The method should return `Optional.empty()` if `collections` is an empty list. Otherwise, the method should return an optional containing one of the *smallest* elements of the list, as judged by the `size()` method that is available on any subtype of `Collection`.
+
+For example, if we have this list of sets available:
+
+ final Set s1 = new HashSet<>(Arrays.asList(1, 2, 3, 4));
+ final Set s2 = new HashSet<>(Arrays.asList(1, 2));
+ final Set s3 = new HashSet<>(Arrays.asList(1, 2, 3, 4));
+ final Set s4 = new HashSet<>(Arrays.asList(3, 4));
+ final List> listOfSetsOfIntegers = Arrays.asList(s1, s2, s3, s4);
+
+then doing:
+
+ final Optional> smallestSet = getSmallestCollection(listOfSetsOfIntegers);
+
+should yield an optional that contains either `s2` or `s4`, as these are the joint-smallest sets in the list of sets.
+
+Your implementation of `getSmallestCollection` should be a one-liner that streams the list and performs an identity-free reduction.
+
+Implement a main method that demonstrates your method in action on a list of sets of integers, and a list of lists of strings.
+
+
+**Hints:** The real challenge here is to work out the right signature for the method `getSmallestCollection`.
+
+* You might try to first implement:
+
+ static Optional> getSmallestCollection(List>) { ... }
+
+ to get your solution working for the explicit case of a list of collections of integers.
+
+* Then you could work out how to make the method work on lists of collections of some arbitrary type `E`.
+
+* Finally, you could work out how to make the method work on a list of any subtype of `Collection` (that's the hardest bit).
diff --git a/questions/17b1.md b/questions/17b1.md
new file mode 100755
index 0000000..2352ea1
--- /dev/null
+++ b/questions/17b1.md
@@ -0,0 +1,100 @@
+[Back to questions](../README.md)
+
+## 17b1: *Default methods*
+
+This question is a synthetic exercise to help you understand the situations where adding a default method to an interface can cause classes that implement that interface to fail to compile.
+
+Take a look at the classes and interfaces under `solutions/code/tutorialquestions/question17b1/beforedefault`.
+
+There are three interfaces, `I`, `J` and `K`.
+
+Interface `I` specifies two overloads of a `foo` method, as well as a `bar` method:
+
+ public interface I {
+
+ int foo();
+
+ int foo(int x);
+
+ int bar(int x);
+
+ }
+
+Interface `J` specifies the same single-argument `foo` and `bar` methods as `I` (but no zero-argument `foo` method):
+
+ public interface J {
+
+ int foo(int x);
+
+ int bar(int y);
+
+ }
+
+Interface `K` extends both `I` and `J`, and adds an additional specification for a zero-argument method, `baz`:
+
+ public interface K extends I, J {
+
+ void baz();
+
+ }
+
+This means that to implement `K`, a class needs to provide methods with all of the following signatures:
+
+ int foo(); // Required by I
+ int foo(int x); // Required by I and J
+ int bar(int x); // Required by I and J
+ void baz(); // Required by K
+
+Now take a look at classes `A`, `B` and `C`.
+
+Class `A` implements `I`, providing implementations of all the required methods, plus of two additional methods with the following signatures:
+
+ public int foobar();
+ public void foobar(int x);
+
+Class `B` implements both `I` and `J`, providing just those methods required by these interfaces.
+
+Class `C` implements `K`, providing implementations of the methods required by `I` and `J` as well as the extra method required by `K`. Additionally, `C` provides two further methods with the following signatures:
+
+ public int foobar() throws IOException;
+ protected int foobar(int x);
+
+**Your task:**
+
+1. Add the following default methods to `I`:
+
+ ```
+ default int foobar() {
+ return bar(foo());
+ }
+
+ default int foobar(int x) {
+ return bar(foo(x));
+ }
+ ```
+
+ Add the following default method to `J`:
+
+ ```
+ default int foobar(int x) {
+ return bar(foo(x));
+ }
+ ```
+
+ Add the following default method to `K`:
+
+ ```
+ default int foobar(int x) {
+ return I.super.foobar(x);
+ }
+ ```
+
+2. You should find that the addition of these methods leads to several compilation problems in the implementing classes `A`, `B` and `C`, due to incompatible method signatures.
+
+ For each such problem, deal with it in one of the following ways:
+
+ * Add a method to the class that is failing to compile in order to resolve the clash. (In this case you can implement the method in any way that respects the method's signature; the details of what the method actually does are not important for this question.)
+
+ * Rename a method in the class that is failing to compile by adding `Original` to the end of its name (so that e.g. `foo` would become `fooOriginal`)
+
+ In each case, write a brief comment explaining why you added or renamed the method.
diff --git a/questions/336b.md b/questions/336b.md
new file mode 100755
index 0000000..9727e22
--- /dev/null
+++ b/questions/336b.md
@@ -0,0 +1,53 @@
+[Back to questions](../README.md)
+
+## 336b: *Evolving the Set interface*
+
+This question involves adding *default* methods to the `GenericSet` interface that you designed in [question b401](b401.md).
+
+*Note:* if you had trouble completing [question b401](b401.md), you have two choices:
+
+* You could attempt this question by starting with the [question b401](b401.md) sample solution;
+
+* You could add default methods to the `IntSet` interface of [question 8a61](8a61.md), rather than to its generic version.
+
+Let's suppose that our `GenericSet` interface has become popular, and that we now regret not having included some additional methods when we designed the interface. We'll go through how to use default methods to fill these omissions.
+
+1. Add a `default` method to the `GenericSet` interface called `addAll`. The `addAll` method have `void` return type, and should take an array, `items`, of type `E` as its single parameter, where `E` is the generic parameter associated with `GenericSet` (use `Integer` or `int` instead of `E` if you are working on `IntSet` rather than `GenericSet`).
+
+ For each element `item` in `items`, the `addAll` method should use the `add` method to add `items` to the set.
+
+2. In a `Demo` class, write a main method that creates a `MemoryEfficientGenericSet` of integers and a `SpeedEfficientGenericSet` of integers, creates some small arrays of integers, and uses `addAll` to add the arrays of integers to each of the sets. Run your `main` method to check that it behaves appropriately.
+
+ Now change your `main` method so that it creates some *large* arrays of integers, and uses `addAll` to add the large arrays of integers to each of the sets.
+
+ (You could look at `Demo.java` from the sample solution for some example code here; see `solutions/code/tutorialquestions/question336b/Demo.java`.)
+
+ You will probably find that `addAll` takes an excessively long time to add a large array of integers to a `MemoryEfficientGenericSet`. Think about why this is.
+
+3. The default implementation of `addAll` works for any set, but as demonstrated in step 2 is not efficient for `MemoryEfficientGenericSet`s. Override `addAll` in `MemoryEfficientGenericSet` with an implementation that does *not* invoke the default `addAll` implementation, but instead uses a `HashSet` local variable to keep track of the contents of the set. This hash set should be initialized with the contents of the `MemoryEfficientGenericSet`. Then, for each element of `items` (the parameter to `addAll`), you should first check whether the element is in the hash set. If it is, you should move on to the next element; otherwise you should add it directly to the elements of the `MemoryEfficientGenericSet` (without going through the `add` method), and also add it to the hash set of elements that are known to be in the set.
+
+ You should find that, with this specific implementation of `addAll`, the code in your `main` method behaves in an efficient manner. Think hard to make sure you understand why this is the case.
+
+4. Add one more default method to `GenericSet`. This method should be called `asUnmodifiableSet`. It should take no parameters, and should return a `GenericSet`. The generic set that is returned should be a version of the original set where any methods that could cause the set to be modified are replaced with methods that throw an `UnsupportedOperationException`.
+
+ There are two ways you could approach this. The simple, but somewhat verbose way, is to make a new class, `UnmodifiableGenericSet` that implements the `GenericSet` interface. This class should have a single field of type `GenericSet` representing the set for which an unmodifiable version is being created; this field could be called `wrapped`. The new class should implement all of the required methods of `GenericSet`. The non-mutating methods should be implemented by delegation to `wrapped`: invoking the corresponding method, with the same parameters (if any) on `wrapped`, and returning the result (if any) returned by said method. The mutating methods should be implemented by simply throwing an `UnsupportedOperationException`.
+
+ Recall from [question 85bb](85bb.md)) that we avoided the need for writing a fully separate iterator class by using an *inner class*. It is not possible for an interface to have an inner class (it can have a *nested class*, but not an inner class; read online about nested classes to understand the difference).
+
+ However, we can use an *anonymous inner class* in the implementation of `asUnmodifiableSet`, by having the body of `asUnmodifiableSet` look like this:
+
+ return new GenericSet() {
+ @Override
+ public void add(E item) {
+ throw new UnsupportedOperationException("Attempt to add to an unmodifiable set.");
+ }
+ ... /* Implementations of other methods */ ...
+ };
+
+ This returns an instance of a new class that implements the `GenericSet` interface, implementing its methods according to the method implementations given between the `{` and `}` following `new GenericSet`.
+
+ If you follow this approach, your anonymous inner class will need to call methods of the `GenericSet` that it is defined inside. This can be achieved by using the syntax `GenericSet.this`. So, for example, in a method inside the anonymous inner class, `isEmpty()` would call the `isEmpty` method of the anonymous inner class, while `GenericSet.this.isEmpty()` would call the `isEmpty` method of the object implementing the outer `GenericSet` interface.
+
+ Try both implementation approaches -- using an explicit `UnmodifiableGenericSet` class and an anonymous inner class, and see which you prefer. Beyond the amount of code you have to write, can you see any pros and cons of the two approaches?
+
+5. Adapt your `main` method so that it uses `asUnmodifiableSet` to create unmodifiable versions of some sets, and check that the unmodifiable sets behave just like the sets that they wrap if non-mutator methods are called, and that appropriate exceptions are thrown if mutator methods are called. Notice that, due to the use of a default method, you can work with unmodifiable versions of `MemoryEfficientGenericSet`s and `SpeedEfficientGenericSet`s without having had to modify these classes.
diff --git a/questions/67dd.md b/questions/67dd.md
index b5abc70..14a1fdd 100755
--- a/questions/67dd.md
+++ b/questions/67dd.md
@@ -25,4 +25,4 @@ Words: 35
Characters: 149
```
-Reading from standard input can be performed as in question [2d33](questions/2d33.md).
+Reading from standard input can be performed as in question [2d33](2d33.md).
diff --git a/questions/68e6.md b/questions/68e6.md
new file mode 100755
index 0000000..b19be2a
--- /dev/null
+++ b/questions/68e6.md
@@ -0,0 +1,74 @@
+[Back to questions](../README.md)
+
+## 68e6: *Using Stream.reduce*
+
+The purpose of this question is to give you practice writing reductions over streams, both with and without identities.
+
+1. Create a class, `Example`, that we will use as a placholder for the static methods that you will write.
+
+2. **List concatenation**: Write a static method with the following signature:
+
+ ```
+ static List concatenate(List> lists);
+ ```
+
+ The method should stream `lists`, to yield a stream of lists of integers, and then should reduce this to a single list, which should be returned. The single list should be the concatenation of all the list. You should use a reduction *with* an identity here, and if `lists` is empty, the result of `concatenate` should be an empty list.
+
+3. **Find min with identity**: Write a static method with the following signature:
+
+ ```
+ static int findMin(List numbers);
+ ```
+
+ The method should stream `numbers` and perform a reduction *with* identity to yield the smallest integer in the list, or the largest possible integer if the list is empty. Your reduction should use a *method reference* as its accumulator.
+
+4. **Find min without identity**: Write a static method with the following signature:
+
+ ```
+ static int findMinOrZero(List numbers);
+ ```
+
+ The method should stream `numbers` and perform a reduction *without* an identity to yield the smallest integer in the list, or zero if the list is empty. Your reduction should use a *method reference* as its accumulator.
+
+5. **Find max with identity**: Write a static method with the following signature:
+
+ ```
+ static int findMax(List numbers);
+ ```
+
+ The method should stream `numbers` and perform a reduction *with* identity to yield the largest integer in the list, or the smallest possible integer if the list is empty. For the sake of practice, your reduction should use a *lambda* as its accumulator (even though a method reference would be more natural).
+
+6. **Find max without identity**: Write a static method with the following signature:
+
+ ```
+ static int findMaxOrZero(List numbers);
+ ```
+
+ The method should stream `numbers` and perform a reduction *without* an identity to yield the largest integer in the list, or zero if the list is empty. For the sake of practice, your reduction should use a *lambda* as its accumulator (even though a method reference would be more natural).
+
+7. **Find min of maxes**: Write a static method with the following signature:
+
+ ```
+ static int findMinOfMaxes(List> listOfLists);
+ ```
+ This method should re-use `findMin` and `findMax` to return the smallest maximum among each list in `listOfLists`.
+
+8. **Demo code**: Write a `main` method to demonstrate that your implementations of these methods works on some example inputs. For example, you could use these list declarations and invocations of the above methods, work out what the expected results should be and check whether your solution does give these results:
+
+ ```
+ final List list1 = Arrays.asList(1, 2, 3, 4, 5, 9);
+ final List list2 = Arrays.asList(1, 10, 100, 1000, 10000);
+ final List list3 = Arrays.asList(6, 7, 8);
+ final List> listOfLists = Arrays.asList(list1, list2, list3);
+
+ final List allIntegers = concatenate(listOfLists);
+ final int maxList1 = findMax(list1);
+ final int minList2 = findMin(list2);
+ final int maxEmpty = findMax(Collections.emptyList());
+ final int minEmpty = findMin(Collections.emptyList());
+ final int maxOrZeroEmpty = findMinOrZero(Collections.emptyList());
+ final int minOrZeroEmpty = findMaxOrZero(Collections.emptyList());
+ final int minOfMaxes = findMinOfMaxes(listOfLists);
+ final int minOfMaxesEmpty = findMinOfMaxes(Collections.emptyList());
+ final int minOfMaxesListOfEmptyLists = findMinOfMaxes(Arrays.asList(Collections.emptyList(), Collections.emptyList()));
+ ```
diff --git a/questions/85bb.md b/questions/85bb.md
index cef50a4..7919f11 100644
--- a/questions/85bb.md
+++ b/questions/85bb.md
@@ -55,7 +55,7 @@ You should now find that `StringStackArray` and `StringStackList` no
longer compile. This is because they do not implement the `iterator` method.
### Step 3
-In order to fix this, you need to create two new classes: `StringStackArray``Iterator`,
+In order to fix this, you need to create two new classes: `StringStackArrayIterator`,
and `StringStackListIterator`. Both classes should implement the `StringStackIterator`
interface. Writing these classes is a little tricky, but once you have them, implementing
the `iterator` method in `StringStackArray` is straightforward: simply
@@ -64,7 +64,7 @@ return a new `StringStackArrayIterator`; implementing `iterator` in
So, how should you implement the iterator classes? There are two choices:
-1. Create a fresh class for each of `StringStackArrayIterator` and `StringStack``ListIterator`.
+1. Create a fresh class for each of `StringStackArrayIterator` and `StringStackListIterator`.
Let us consider the `StringStackArrayIterator` case.
`StringStackArrayIterator` should have a field of type `String[]`: a reference to the contents of the stack. In addition, this class should have a field of type `int` that refers to the stack element that the iterator is currently pointing to. This should be initialised to the top of the stack. This solution is OK, but it requires the internal details of a `StringStackArray` to be indirectly exposed to the separate `StringStackArrayIterator` class, since a `StringStackArrayIterator` is constructed using the internals of a `StringStackArray`. More importantly, this setup allows clients to construct instances of `StringStackArrayIterator` *independently* of any actual `StringStackArray`. This doesn't really make sense. The extent of this problem can be limited by making the `StringStackArrayIterator` only package visible.
diff --git a/questions/888a.md b/questions/888a.md
new file mode 100755
index 0000000..d52347a
--- /dev/null
+++ b/questions/888a.md
@@ -0,0 +1,34 @@
+[Back to questions](../README.md)
+
+## 888a: *Generic methods with streams*
+
+The purpose of this question is to give you practice writing some static methods that use one or more *generic* parameters, and that also use streams.
+
+1. **Immutable pair.** Write a generic class, `ImmutablePair`, with two generic parameters, `S` and `T` say, that allows the construction of an (`S`, `T`) pair, and retrieval of its first and second components. Override `toString()` so that a pair is represented as a string of the form `(`*string representation of first element*`, ` *string representation of second element*`)`.
+
+2. **Placeholder class.** Create a class, `Example`, to serve as a placeholder for the static methods you will write.
+
+3. **Generic list concatenation** Recall the `concatenate` function from [question 68e6](questions/68e6.md), which had the signature:
+
+ ```
+ static List concatenate(List> lists);
+ ```
+
+ and returned the concatenation of the lists in `lists`. Observe that there is nothing `Integer`-specific about the notion of concatenating lists.
+
+ Write a version of `concatenate` that is generic with respect to some type `T`, and which takes a list of lists of type `List>`, and returns a `List` that is the concatenation of these lists.
+
+4. **Zip lists of different lengths** Write a static method, `zip`, that is generic with respect to two parameters, `S` and `T`, and that takes two parameters, a list `first` of type `List` and a list `second` of type `List`. The method should return a list of type `List, Optional>>`. At each index *i* less than the minimum size of `first` and `second`, the result should have a pair of present optionals corresponding to the elements at indices *i* of `first` and `second`. At each index *j* (if any) at least the minimum size of `first` and `second` and less than the maximum size of `first` and `second`, the result should have a pair consisting of one present optional and one empty optional; the present optional should be the value at index *j* of the input list, which must exist.
+
+The `zip` function is not a good fit for using streams, so feel free to use a loop-based solution. If you are interested in playing more with streams, then consider using the `IntStream.range()` method to get a stream of integers as long as the maximum size of the two lists, and then use `mapToObj` to map each integer to the appropriate pair, by `get`ting list elements when they are needed.
+
+5. **Flattening pairs of optionals** Write a static method, `flatten`, that is generic with respect to two types, `S` and `T`. The `flatten` method should take three parameters: a list, `maybePairs` of type `List, Optional>>`, an element `defaultS` of type `S`, and an element `defaultT` of type `T`. `flatten` should return a list of type `List>` such that:
+
+ * if an element `s` of type `S` is present as the first component of the pair at index *i* of `maybePairs` present, the first component of the pair at index *i* of the result list is `s`, otherwise it is `defaultS`;
+ * if an element `t` of type `T` is present as the first component of the pair at index *i* of `maybePairs` present, the first component of the pair at index *i* of the result list is `t`, otherwise it is `defaultT`.
+
+ The idea is that the method *flattens* a list of pairs of optionals into a list of pairs, using the given default values wherever an empty optional appears.
+
+ Try to write this method using streams, rather than loops.
+
+6. **Writing a main method** Add a `main` method to `Example` that demonstrates your example methods in action on some example data.
\ No newline at end of file
diff --git a/questions/9a9b.md b/questions/9a9b.md
index 9c6ebbd..cbb10b0 100644
--- a/questions/9a9b.md
+++ b/questions/9a9b.md
@@ -75,7 +75,7 @@ structure that will represent these elements! The trick is to exploit the fact
Turning tune elements into strings will involve overriding `toString`
in your `Note` and `Rest` classes (and possibly also in enumerations you may have created). A quarter-length
-"middle C" note should be represented by the string `C-4(1/4)`. A half-length rest should be represented as
+"middle C" note should be represented by the string `C4(1/4)`. A half-length rest should be represented as
`Rest(1/2)`. Other notes and rests should be represented similarly. The well-known tune Fr\`{e}re-Jaques
starting on "middle C" would be represented as a string thus (with some line breaks added):
diff --git a/questions/b33f.md b/questions/b33f.md
new file mode 100755
index 0000000..5846720
--- /dev/null
+++ b/questions/b33f.md
@@ -0,0 +1,118 @@
+[Back to questions](../README.md)
+
+## b33f: *Logging using a functional interface*
+
+This question will give you practice with quite a range of Java features! You are going to write two functional interfaces: a *logger* interface, with a method for logging a message at a given severity level, and a *string inspector* interface, with a method that takes a string and optionally returns a (severity level, message) pair related to the string's content.
+
+You are also going to implement a couple of loggers and string inspectors, using direct implementations and lambdas. The problem will illustrate how we can use interfaces to provide abstract notions of logging and string inspection, and implement an algorithm that can inspect all lines of a file and log the results, and that we can instantiate this algorithm with various different notions of string inspection and various different methods for logging.
+
+1. **Immutable pair.** [Note: if you tried [question 888a](../questions/888a.md) you may copy in the `ImmutablePair` class implemented as part of that question.] Write a generic class, `ImmutablePair`, with two generic parameters, `S` and `T` say, that allows the construction of an (`S`, `T`) pair, and retrieval of its first and second components. Override `toString()` so that a pair is represented as a string of the form `(`*string representation of first element*`, ` *string representation of second element*`)`.
+2. **Log level enum.** Write an enumeration class, `LogLevel`, specifying the following severity levels, in increasing order of severity:
+ - `VERBOSE`
+ - `INFO`
+ - `WARNING`
+ - `ERROR`
+ - `FATAL`
+3. **Logger functional interface.** Write a functional interface, `Logger`, specifying one method:
+ `void log(LogLevel logLevel, String message);` Use an annotation to indicate that this is a functional interface. Check that, with your annotation, you get a compiler error if you try to add another method to the interface.
+4. **String inspector functional interface.** Write a functional interface, `StringInspector`, specifying a single method, `inspect`, that takes a string and returns, optionally, an `ImmutablePair` comprised of a `LogLevel` and a string.
+
+ The idea is that this method will inspect the input string, returning nothing (i.e. an empty optional) if the string is not noteworthy, and a severity level and message if the string is noteworthy. It is up to implementations of this interface to decide what "noteworthy" means in practice.
+5. **File inspector class.** Write a class, `FileInspector`, with a single field of type `Logger`, a constructor that populates this field with a given `Logger` reference, and a single method, `inspectFile`, that takes a file name (string) and a `StringInspector`.
+
+ The method should open the file with the given name, and process it line by line. It should invoke the string inspector for each line, and if the string inspector reports that the line is noteworthy, it should invoke the logger with the severity level and message reported by the string inspector.
+
+ You can declare the `inspectFile` method should be declared as throwing `IOException`, to avoid having to deal with exceptions, but feel free to experiment with using a `try...catch` block, e.g. to ignore exceptions, or log a fatal error if an exception occurs.
+
+6. **File logger implementation.** Write a class, `FileLogger`, that implements the `Logger` interface. This class should have a `BufferedWriter` field, and should be constructed with a file name: the file name should be used to create a buffered reader from a file reader for the given file, throwing an `IOException` when something goes wrong.
+
+ The `log` method should do nothing if the given severity level is `INFO` or `VERBOSE`. Otherwise it should write a line to the buffered writer of the form "*SEVERITY_LEVEL*`:` *message*", where *SEVERITY_LEVEL* is the string representation of the given severity level and *message* is the message string.
+
+ If an `IOException` occurs during the execution of `log`, it should be ignored.
+
+ The class should also have a `close` method that closes the buffered writer, throwing an `IOException` if something goes wrong.
+
+7. **Known words string inspector implementation.** Write a class, `KnownWordsStringInspector`, that implements the `StringInspector` interface. As fields, this class should have a set of *error words* and a set of *verbose words*, each of which should be a set of strings, and the class should be constructed with a reference with which to populate each of these fields.
+
+ The `inspect` method should split the input string using " ", turning it into an array of strings, and iterate over these strings. Each string that occurs in the set of *error words* should be added to a list of *observed* error words. Each string that occurs in the set of *verbose words* should be added to a list of *observed* verbose words. If no error or verbose words are observed, the method should return an empty optional. Otherwise, a (severity level, message) pair should be returned (packaged in an optional). The severity level should be `ERROR` if at least one error word was observed, and `VERBOSE` otherwise. The message should indicate which error words and which verbose words were observed.
+
+8. **Writing a demo, using these classes and using some lambdas.** Write a class, `Demo`, with a main method.
+
+ In the main method, check that exactly three arguments are supplied; these should correspond to an input file and two output files.
+
+ Declare a local variable, `stderrLogger`, of type `Logger`, that implements the functional interface `Logger` via a lambda:
+
+ final Logger stderrLogger = (logLevel, message) -> { ... };
+
+ The body of this lambda should write `message` to standard error, prefixed by `***IMPORTANT***\n` if the severity level is `WARNING` or higher, and prefixed by `*NOTE*\n` otherwise.
+
+ Similarly, declare a local variable, `lineTooLongInspector`, of type `StringInspector`, that implements the functional interface `StringInspector` via a lambda. The lambda should return an optional containing a pair of the form (`ERROR`, "Line too long: *line*") if the given string is more than 100 characters in length, and should return an empty optional otherwise.
+
+ Create three `FileInspector` objects:
+
+ - A `FileInspector` that takes `stderrLogger` as its logger
+ - Two `FileInspectors` that each take a `FileLogger` as their logger, using `args[1]` and `args[2]` as the output file names
+
+ Create a `KnownWordsStringInspector` with some error and verbose words of your choice.
+
+ Use the following statements to run your file inspectors on the file with name `args[0]`, using your string inspectors:
+
+ System.err.println("Starting inspection 1:");
+ inspector1.inspectFile(args[0], lineTooLongInspector);
+ System.err.println("\nStarting inspection 2:");
+ inspector1.inspectFile(args[0], knownWordsInspector);
+ System.err.println("\nStarting inspection 3 (see " + args[1] + " for details).");
+ inspector2.inspectFile(args[0], lineTooLongInspector);
+ System.err.println("\nStarting inspection 4 (see " + args[2] + " for details).");
+ inspector3.inspectFile(args[0], knownWordsInspector);
+
+ At the end of `main`, be sure to close the two `FileLogger` instances that you created.
+
+9. **Running your program.** For my "known words" string inspector I used "goto", "finalize" and "static" as the error words, and "continue" and "break" as the verbose words. I populated "input.txt" with the following:
+
+ the quick brown fox jumped over the lazy dog
+ a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me?
+ Dijkstra said that goto is considered harmful; OS developers disagree
+ I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break
+ But for now I will continue to continue until that break comes
+
+ I then ran this command to execute the program:
+
+ java tutorialquestions.questionb33f.Demo input.txt output1.txt output2.txt
+
+ which gave the following output (the output your program gives may of course be a bit different from this, depending on exactly how you chose to emit strings):
+
+ Starting inspection 1:
+ *** IMPORTANT ***
+ Line too long: a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me?
+ *** IMPORTANT ***
+ Line too long: I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break
+
+ Starting inspection 2:
+ *** IMPORTANT ***
+ Observed the following error words: [goto]
+
+ *** IMPORTANT ***
+ Observed the following error words: [goto, finalize]
+ Observed the following words of note: [continue, break]
+
+ * NOTE *
+ Observed the following words of note: [continue, continue, break]
+
+
+ Starting inspection 3 (see output1.txt for details).
+
+ Starting inspection 4 (see output2.txt for details).
+
+ Contents of "output1.txt":
+
+ ERROR: Line too long: a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me?
+ ERROR: Line too long: I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break
+
+ Contents of "output2.txt":
+
+ ERROR: Observed the following error words: [goto]
+
+ ERROR: Observed the following error words: [goto, finalize]
+ Observed the following words of note: [continue, break]
+
\ No newline at end of file
diff --git a/questions/b4a5.md b/questions/b4a5.md
index 1f5a8fc..6ad29a0 100755
--- a/questions/b4a5.md
+++ b/questions/b4a5.md
@@ -10,17 +10,12 @@ public void finalize();
which is called when an object is garbage-collected.
-A developer can override `finalize` to do some house-keeping or sanity checking when an object is disposed of. For example, `finalize`
-could be used to ensure that an object has released resources such as file handles before the object is deallocated.
+This method was deprecated in Java 9. (See [this post](https://stackoverflow.com/a/56454348) for some interesting insights into why it was deprecated.) As a result, you should not use `finalize` in your code.
-Typically `finalize` is used for defensive programming: to guard against unintentional errors (e.g., forgetting to close a file)
-that may have occurred elsewhere in
-the program.
-
-In this question, we will use `finalize` to observe the behaviour of the garbage collector.
+Neverthelss, experimenting with `finalize` can provide interesting insights into how garbage collection works, and in this question, we will use `finalize` to observe the behaviour of the garbage collector.
**Disclaimer:** This exercise is purely educational, to let you see the garbage collector in action,
-and to get more experience writing simple Java programs. You should *never* write a real program that abuses `finalize` in this way!
+and to get more experience writing simple Java programs. You should likely never write a real program that uses `finalize`, since it is deprecated, and you should *never* write a real program that abuses `finalize` in this way!
Write a class, `A`, with a private `int` field called `id`. `A` should have a single constructor, which
should take an integer parameter used to initialise `id`.
diff --git a/questions/d3f5.md b/questions/d3f5.md
new file mode 100755
index 0000000..d5a3f0f
--- /dev/null
+++ b/questions/d3f5.md
@@ -0,0 +1,27 @@
+[Back to questions](../README.md)
+
+## d3f5: *Streams and downcasting*
+
+In an `Example` class, write a method `restrictToPositiveIntegers` with the following signature:
+
+ static Stream restrictToPositiveIntegers(Stream numbers);
+
+Given a stream of `Number`s, the method should return the substream of this stream comprised of only those numbers of type `Integer` whose integer value is positive.
+
+The body of `restrictToPositiveIntegers` should be a single statement, and it should *not* convert the input stream to a list or any other collection.
+
+Write a `main` method showing that if you create a `List` called `numbers` populated with various `Integer`, `Float`, `Double` and `Short` values, and then do:
+
+ System.out.println("Positive integers from " + numbers + " are: "
+ + restrictToPositiveIntegers(numbers.stream()).collect(Collectors.toList()));
+
+you see precisely the positive integers from your list of numbers being output.
+
+Try creating a `List` or `List` and invoking `restrictToPositiveIntegers` on a stream derived from each of these lists. You should find that this does not compile.
+
+**Advanced:** Write a method, `restrictToPositiveIntegersBoundedWildcard`, that can take a stream of any type `T` that is `Number` or one of `Number`'s subclasses and return the substream containing only those positive integers from the original stream.
+
+**Hints**
+
+* You will need to use instance testing and downcasting in the implementation of `restrictToPositiveIntegers`.
+* For `restrictToPositiveIntegersBoundedWildcard`, the clue is in the name!
\ No newline at end of file
diff --git a/questions/dd4c.md b/questions/dd4c.md
index 1eec6cc..cbda215 100644
--- a/questions/dd4c.md
+++ b/questions/dd4c.md
@@ -35,6 +35,7 @@ Clock 1 shows: 6 seconds since midnight. Clock 2 shows: 23:59:59
Clock 1 shows: 7 seconds since midnight. Clock 2 shows: 00:00:00
Clock 1 shows: 8 seconds since midnight. Clock 2 shows: 00:00:01
Clock 1 shows: 9 seconds since midnight. Clock 2 shows: 00:00:02
+...
```
Now design a class, `AlarmClock`, which extends `Clock` by storing a specific time
diff --git a/questions/f79b.md b/questions/f79b.md
index 46ff75d..86a64dd 100755
--- a/questions/f79b.md
+++ b/questions/f79b.md
@@ -5,7 +5,7 @@
A number *x* is a *perfect palindromic cube* if it is a perfect cube (i.e., *x*=*y*3 for some integer *y*), and it is a palindrome when
written in decimal (i.e., it appears the same backwards). For example, 1030301 is a perfect palindromic cube: it is clearly a palindrome, and 1013 = 1030301.
-Write a program that enumerates the first 2000 non-negative integers, and indicates which integers yield palindromic numbers when they are cubed. The output of your program should begin:
+Write a program that enumerates the first 1500 non-negative integers, and indicates which integers yield palindromic numbers when they are cubed. The output of your program should begin:
```
0 cubed is 0
@@ -14,6 +14,8 @@ Write a program that enumerates the first 2000 non-negative integers, and indica
7 cubed is 343
```
+Think about whether or not your program would work properly if you continued significantly larger non-negative integers. If it would not, why not?
+
**Hints:** You may find the `length` and `charAt` methods of the `String` class useful.
These are *instance methods*: you invoke them on a `String` object. Also,
you may find the `String.valueOf` method helpful to turn an `int` into a `String`. This is
diff --git a/questions/fe94.md b/questions/fe94.md
new file mode 100755
index 0000000..afeb490
--- /dev/null
+++ b/questions/fe94.md
@@ -0,0 +1,30 @@
+[Back to questions](../README.md)
+
+## fe94: *Using Stream.map and Stream.filter*
+
+This question is about writing static methods that use the `map` and `filter` operations on streams.
+
+1. Create a class, `Example`, that we will use as a placeholder for the static methods that you will write.
+
+2. Write a static method with the following signature:
+
+ static List reverseEachString(List input);
+
+ This method should return a list whose contents are identical to the strings in `input`, except that each string should be reversed. For example, if you invoked `reverseEachString` on the list `[ "hello", "world" ]`, you should get back the list `[ "olleh", "dlrow" ]`.
+
+ Your implementation should comprise a *single* statement that should get a stream from the list, apply multiple map operations to the stream, and collect the result back into a list.
+
+ In particular, you should map the constructor of `StringBuilder` over the stream, to get a stream of `StringBuilder`s, then map the `reverse` method of `StringBuilder` over this stream, then map the `toString` method of `StringBuilder` over the resulting stream to get a stream of `String`s again.
+
+ 3. Now write an alternative version of `reverseEachString`, called `reverseEachStringMonolithic`. This should behave in a functionally identical manner to `reverseEachString`, but instead of applying multiple map operations, a *single* map operation should be used that combines the effects of all of the map operations you used in `reverseEachString`.
+
+4. Write a static method with the following signature:
+
+ static List sqrtsOfFirstDigits(List input);
+
+ This method should turns `input` into a stream, filter to just those strings that start with a digit, map each remaining string to the integer corresponding to its first digit, and then map each such integer to its square root via the `Math.sqrt` function, returning the resulting stream collected as a list.
+
+5. Now write an alternative version of `sqrtsOfFirstDigits`, called `sqrtsOfFirstDigitsMonolithic`. This should have the same behaviour as `sqrtsOfFirstDigits`, but should use just one call to `map` and one call to `filter` (as well as the usual calls to `stream` and `collect`).
+
+6. Write a `main` method to demonstrate that your static methods work on some example inputs.
+
diff --git a/questions/hex_strings.md b/questions/hex_strings.md
index d273fd8..168c4e7 100755
--- a/questions/hex_strings.md
+++ b/questions/hex_strings.md
@@ -2,17 +2,9 @@
There were originally 500 hex strings. Inflicting more than 500 questions on the first year students would not be nice!
-336b
-17b1
-11e2
-b33f
-fe94
d046
-68e6
c42e
0785
-d3f5
-888a
3499
e900
359f
diff --git a/solutions/014e.md b/solutions/014e.md
index b5e8740..69ad897 100644
--- a/solutions/014e.md
+++ b/solutions/014e.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [014e](../questions/014e): *Random numbers*
+## Solution to [014e](../questions/014e.md): *Random numbers*
See code at `solutions/code/tutorialquestions/question014e`
diff --git a/solutions/0378.md b/solutions/0378.md
index b0cfc62..aded901 100644
--- a/solutions/0378.md
+++ b/solutions/0378.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [0378](../questions/0378): *Comparing people*
+## Solution to [0378](../questions/0378.md): *Comparing people*
See code at `solutions/code/tutorialquestions/question0378`
diff --git a/solutions/0c21.md b/solutions/0c21.md
index 45efc97..41a7e87 100644
--- a/solutions/0c21.md
+++ b/solutions/0c21.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [0c21](../questions/0c21): *Properties*
+## Solution to [0c21](../questions/0c21.md): *Properties*
See code at `solutions/code/tutorialquestions/question0c21`
@@ -32,6 +32,10 @@ classes are available in the `instanceofsolution` package (the name of this pack
class that extends `House`. Then have `DetachedHouse` extend `House` and implement `Detached`,
and have `DetachedBungalow` extend `Bungalow` and implement `Detached` (the setup for the semi-detached
and terraced property types is analogous). You might benefit from drawing a class diagram for this arrangement.
+However, a problem with this solution is that while a `DetachedBungalow` is an instance of `Bungalow`, `House` and `Detached`, it is not an instance of `DetachedHouse`.
+This may be undesirable if we would like to have an is-a relationship between `DetachedBungalow`s and `DetachedHouse`s.
+A similar problem affects semi-detached and terraced bungalows,
+and for this reason if you implement the second solution you will find that you don't get quite the number of terraced houses when you implement your `main` method.

diff --git a/solutions/0f05.md b/solutions/0f05.md
index d223618..a32a7f9 100644
--- a/solutions/0f05.md
+++ b/solutions/0f05.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [0f05](../questions/0f05): *Coloured points*
+## Solution to [0f05](../questions/0f05.md): *Coloured points*
See code at `solutions/code/tutorialquestions/question0f05`
diff --git a/solutions/1171.md b/solutions/1171.md
index 91e8617..24702cc 100755
--- a/solutions/1171.md
+++ b/solutions/1171.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [1171](../questions/1171): *Cloning graphs*
+## Solution to [1171](../questions/1171.md): *Cloning graphs*
See code at `solutions/code/tutorialquestions/question1171`
diff --git a/solutions/11e2.md b/solutions/11e2.md
new file mode 100755
index 0000000..e21fc17
--- /dev/null
+++ b/solutions/11e2.md
@@ -0,0 +1,21 @@
+[Back to questions](../README.md)
+
+## Solution to [b33f](../questions/11e2.md): *Bounded generic methods with streams*
+
+See code at `solutions/code/tutorialquestions/question11e2`
+
+Compare the sample source code for this question with your solution.
+
+The signature of the method is:
+
+ public static > Optional getSmallestCollection(List collections);
+
+Unpicking this a bit, the `E` is the element type of the collections in the list of collections. A simpler method signature is:
+
+ public static Optional> getSmallestCollection(List> collections);
+
+but this only works if we pass in a `List>`; we cannot pass in a `List>` for example.
+
+The purpose of `C extends Collection` is to say that "`C` is any type that is a subtype of `Collection`, or `Collection` itself". The true method signature is the same as the simplified method signature, except that `C extends Collection` is introduced right after `E` is introduced, and then `C` is used instead of `Collection` thereafter.
+
+The solution has a couple of comments starting: "Think about why ..." -- take a look at these and have a think!
diff --git a/solutions/1486.md b/solutions/1486.md
index f537bc2..a4e0f62 100644
--- a/solutions/1486.md
+++ b/solutions/1486.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [1486](../questions/1486): *String stack*
+## Solution to [1486](../questions/1486.md): *String stack*
See code at `solutions/code/tutorialquestions/question1486`
diff --git a/solutions/153d.md b/solutions/153d.md
index 8a037a7..f30ab8c 100644
--- a/solutions/153d.md
+++ b/solutions/153d.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [153d](../questions/153d): *Exceptions and inheritance (iii)*
+## Solution to [153d](../questions/153d.md): *Exceptions and inheritance (iii)*
See code at `solutions/code/tutorialquestions/question153d`
diff --git a/solutions/17b1.md b/solutions/17b1.md
new file mode 100755
index 0000000..7e7863f
--- /dev/null
+++ b/solutions/17b1.md
@@ -0,0 +1,77 @@
+[Back to questions](../README.md)
+
+## Solution to [17b1](../questions/17b1.md): *Default methods*
+
+A possible solution is provided via the classes and interfaces under `solutions/code/tutorialquestions/question17b1/afterdefault`.
+
+* **Class `A`**: This class implements `I`, so possible problems come from the addition of these default methods to `I`:
+
+ ```
+ default int foobar() {
+ return bar(foo());
+ }
+
+ default int foobar(int x) {
+ return bar(foo(x));
+ }
+ ```
+
+ The zero-argument version of `foobar` is not a problem, since `A` already contains a method with exactly this signature. This overrides the default implementation, and the compiler is happy.
+
+ However, `A` already contained a method with the following signature:
+
+ ```public void foobar(int x);```
+
+ This is a problem because it has the same argument type as the one-argument default method that has been added to `I`, but a different return type. Overloading on return types is not allowed in Java, so the compiler is not happy. In the sample solution this is dealt with by renaming `foobar` to `foobarOriginal` in `A` (as required by the question).
+
+* **Class `B`**: This class implements both `I` and `J`, so possible problems come from the fact that the following default methods have been added to `I`:
+
+ ```
+ default int foobar() {
+ return bar(foo());
+ }
+
+ default int foobar(int x) {
+ return bar(foo(x));
+ }
+ ```
+
+ and the following to `J`:
+
+ ```
+ default int foobar(int x) {
+ return bar(foo(x));
+ }
+ ```
+
+ The problem is that `B` does not provide a method with signature:
+
+ ```public int foobar(int x);```
+
+ and both `I` and `J` provide a default method with this signature (remember that interface methods are implicitly public). It is ambiguous which of these methods `B` should inherit, so the compiler is unhappy.
+
+ The solution is to add an implementation of `foobar` with this signature to `B`. As explained in the question, any implementation that respects the method signature is fine; in the sample solution I have provided a `foobar` that uses `return I.super.foobar(x);` to choose the default implementation of `foobar` coming from `I`.
+
+* **Class `C`**: This class implements `K`, which extends `I` and `J`. Problems could therefore come from the default methods added by `I` and `J` (described above), and the following default method added by `K`:
+
+ ```
+ default int foobar(int x) {
+ return I.super.foobar(x);
+ }
+ ```
+
+ The problem we had with class `B`, where it was unclear which `int foobar(int x)` method should be chosen given the default provided by `I` and `J`, is not a problem here since `K` provides the above default method of this signature, which resolves the ambiguity (by choosing `I`'s `foobar`).
+
+ However, there are two problematic methods in `C`, with these signatures:
+
+ ```
+ public int foobar() throws IOException;
+ protected int foobar(int x);
+ ```
+
+ The zero-argument `foobar` is problematic because it declares that it can throw an `IOException`, while the default zero-argument `foobar` specified by `I` does not claim to throw any exceptions. This is illegal: an implementing class cannot implement an interface method in a manner that throws *more* exceptions than the original. This would be bad because client code written against the interface would not be prepared for these additional exceptions. (Throwing fewer exceptions, in contrast, is fine.)
+
+ The one-argument `foobar` is problematic because it has more restricted visibility than the one-argument `foobar` specified in each of `I`, `J` and `K`. An implementing class is not allowed to *decrease* the visibility of an interface method that it implements, and here we have an attempt to decrease visibility from public (recall that interface methods are always public) to protected.
+
+ As required by the question, the solution is to rename each of these troublesome `foobar` methods to `foobarOriginal`.
+
\ No newline at end of file
diff --git a/solutions/1ae9.md b/solutions/1ae9.md
index 82158f5..1a70947 100644
--- a/solutions/1ae9.md
+++ b/solutions/1ae9.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [1ae9](../questions/1ae9): *Reusing immutable value objects*
+## Solution to [1ae9](../questions/1ae9.md): *Reusing immutable value objects*
See code at `solutions/code/tutorialquestions/question1ae9`
diff --git a/solutions/1aeb.md b/solutions/1aeb.md
index 5f9d2ad..9310410 100644
--- a/solutions/1aeb.md
+++ b/solutions/1aeb.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [1aeb](../questions/1aeb): *Generic number manipulation*
+## Solution to [1aeb](../questions/1aeb.md): *Generic number manipulation*
See code at `solutions/code/tutorialquestions/question1aeb`
diff --git a/solutions/236b.md b/solutions/236b.md
index 08596c7..13fd64d 100644
--- a/solutions/236b.md
+++ b/solutions/236b.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [236b](../questions/236b): *Fields for properties*
+## Solution to [236b](../questions/236b.md): *Fields for properties*
See code at `solutions/code/tutorialquestions/question236b`
diff --git a/solutions/2862.md b/solutions/2862.md
index df2b99d..1774f31 100644
--- a/solutions/2862.md
+++ b/solutions/2862.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [2862](../questions/2862): *Exceptions and inheritance (ii)*
+## Solution to [2862](../questions/2862.md): *Exceptions and inheritance (ii)*
See code at `solutions/code/tutorialquestions/question2862`
diff --git a/solutions/290b.md b/solutions/290b.md
index cd57755..e2d965c 100644
--- a/solutions/290b.md
+++ b/solutions/290b.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [290b](../questions/290b): *Memory leaks in Java*
+## Solution to [290b](../questions/290b.md): *Memory leaks in Java*
See code at `solutions/code/tutorialquestions/question290b`
diff --git a/solutions/2d33.md b/solutions/2d33.md
index f4ec1ed..fecef5e 100644
--- a/solutions/2d33.md
+++ b/solutions/2d33.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [2d33](../questions/2d33): *Reversed order of input*
+## Solution to [2d33](../questions/2d33.md): *Reversed order of input*
See code at `solutions/code/tutorialquestions/question2d33`
diff --git a/solutions/2ffc.md b/solutions/2ffc.md
index 9f8ca62..a4551d3 100644
--- a/solutions/2ffc.md
+++ b/solutions/2ffc.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [2ffc](../questions/2ffc): *Generic stacks*
+## Solution to [2ffc](../questions/2ffc.md): *Generic stacks*
See code at `solutions/code/tutorialquestions/question2ffc`
diff --git a/solutions/30cd.md b/solutions/30cd.md
index 7bbbd3c..b1cd717 100644
--- a/solutions/30cd.md
+++ b/solutions/30cd.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [30cd](../questions/30cd): *Heap exhaustion*
+## Solution to [30cd](../questions/30cd.md): *Heap exhaustion*
See code at `solutions/code/tutorialquestions/question30cd`
diff --git a/solutions/336b.md b/solutions/336b.md
new file mode 100755
index 0000000..b02479b
--- /dev/null
+++ b/solutions/336b.md
@@ -0,0 +1,27 @@
+[Back to questions](../README.md)
+
+## Solution to [336b](../questions/336b.md): *Evolving the Set interface*
+
+See code at `solutions/code/tutorialquestions/question336b`
+
+Compare your solution with the sample solution.
+
+The default `addAll` method is straightforward:
+
+ default void addAll(E[] items) {
+ for (E item : items) {
+ add(item);
+ }
+ }
+
+From a performance point of view, its potential downfall is that it invokes `add` as many times as there are elements in `items`. To see the problem, suppose that `items` has size *N* and the set to which these items is being added to is initially empty. For the first few additions, the call to `add` in `addAll` will iterate over an empty or close-to-empty set. But as the number of elements in the set (thanks to having added many elements from `items`) approaches *N*, each call to `add` for the final elements of `items` will require iterating through close to *N* existing elements, giving *O(N2)* time complexity.
+
+This explains why, for `MemoryEfficientGenericSet`, the code in your `main` method should have performed so poorly before you implemented a specialised version of `addAll` for this kind of set, because `MemoryEfficientGenericSet` stores set elements in a list, and adding a set element involves iterating over this entire list to see whether the element is already present.
+
+The more targeted implementation of `addAll` for `MemoryEfficientGenericSet` overcomes this problem by temporarily using a `HashSet`, for which adding and lookup of elements is very efficient, to decide which elements are not already in the set. Using this hash set slightly goes against the memory-efficient intentions of the `MemoryEfficientGenericSet` class, but not too much since the hash set is only alive for the duration of the `addAll` method: it is not the case that every `MemoryEfficientGenericSet` needs to have an accompanying hash set alive at all times.
+
+For the `asUnmodifiableSet` method, the sample solution shows the use of an anonymous inner class.
+
+A potential pro of using an anonymous inner class (beyond how much code you need to write), is that this class is *not* available to any other classes in your program. If you write a separate class, `UnmodifiableGenericSet`, you can give this class package visibility so that it is visible only to the package that contains the `GenericSet` interface, but you cannot stop other classes in this package from being able to create their own `UnmodifiableGenericSet` instances. The anonymous class approach makes it impossible for other code to create instances of the class, because the class has no name that could be used in conjunction with `new`.
+
+This is also a potential con of the approach: there could be a case where you would *like* the class that implement an unmodifiable version of the `GenericSet` interface to be more broadly available, in which case it would need to be in a separate class.
\ No newline at end of file
diff --git a/solutions/4c70.md b/solutions/4c70.md
index 2c22ae2..0c8d409 100644
--- a/solutions/4c70.md
+++ b/solutions/4c70.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [4c70](../questions/4c70): *Lottery numbers*
+## Solution to [4c70](../questions/4c70.md): *Lottery numbers*
See code at `solutions/code/tutorialquestions/question4c70`
diff --git a/solutions/5235.md b/solutions/5235.md
index ea10ca5..5ac4824 100644
--- a/solutions/5235.md
+++ b/solutions/5235.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [5235](../questions/5235): *Equality between points*
+## Solution to [5235](../questions/5235.md): *Equality between points*
See code at `solutions/code/tutorialquestions/question5235`
diff --git a/solutions/5566.md b/solutions/5566.md
index 217b3c6..09c03af 100644
--- a/solutions/5566.md
+++ b/solutions/5566.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [5566](../questions/5566): *Exception-throwing stacks*
+## Solution to [5566](../questions/5566.md): *Exception-throwing stacks*
See code at `solutions/code/tutorialquestions/question5566`
diff --git a/solutions/5981.md b/solutions/5981.md
index fffff82..0a76302 100644
--- a/solutions/5981.md
+++ b/solutions/5981.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [5981](../questions/5981): *Shapes*
+## Solution to [5981](../questions/5981.md): *Shapes*
See code at `solutions/code/tutorialquestions/question5981`
diff --git a/solutions/5d30.md b/solutions/5d30.md
index a51d494..f4c6afb 100644
--- a/solutions/5d30.md
+++ b/solutions/5d30.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [5d30](../questions/5d30): *Unreliable buffered reader*
+## Solution to [5d30](../questions/5d30.md): *Unreliable buffered reader*
See code at `solutions/code/tutorialquestions/question5d30`
diff --git a/solutions/6346.md b/solutions/6346.md
index 932a137..354afab 100644
--- a/solutions/6346.md
+++ b/solutions/6346.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [6346](../questions/6346): *Depth of arithmetic expressions*
+## Solution to [6346](../questions/6346.md): *Depth of arithmetic expressions*
See code at `solutions/code/tutorialquestions/question6346`
diff --git a/solutions/67dd.md b/solutions/67dd.md
index 07be0bc..d04c025 100644
--- a/solutions/67dd.md
+++ b/solutions/67dd.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [67dd](../questions/67dd): *Word count*
+## Solution to [67dd](../questions/67dd.md): *Word count*
See code at `solutions/code/tutorialquestions/question67dd`
diff --git a/solutions/68e6.md b/solutions/68e6.md
new file mode 100755
index 0000000..f1bbc0b
--- /dev/null
+++ b/solutions/68e6.md
@@ -0,0 +1,11 @@
+[Back to questions](../README.md)
+
+## Solution to [68e6](../questions/68e6.md): *Using Stream.reduce*
+
+See code at `solutions/code/tutorialquestions/question68e6`
+
+Compare your solution to the sample code.
+
+Note the use in the sample code of `Integer.MAX_VALUE` and `Integer.MIN_VALUE` to get the largest and smallest representable integer values, respectively.
+
+That `findMinOfMaxes` works by mapping `findMax` over a streamed version of `listOfLists`. It is necessary to collect the result of this map operation back to a list in order to invoke `findMin` on the result.
diff --git a/solutions/7041.md b/solutions/7041.md
index 41f5ca2..8361621 100644
--- a/solutions/7041.md
+++ b/solutions/7041.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [7041](../questions/7041): *Cloning tree nodes*
+## Solution to [7041](../questions/7041.md): *Cloning tree nodes*
See code at `solutions/code/tutorialquestions/question7041`
diff --git a/solutions/710c.md b/solutions/710c.md
index 395b3bb..0ddb637 100644
--- a/solutions/710c.md
+++ b/solutions/710c.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [710c](../questions/710c): *The consequences of overriding `equals`*
+## Solution to [710c](../questions/710c.md): *The consequences of overriding `equals`*
See code at `solutions/code/tutorialquestions/question710c`
diff --git a/solutions/7206.md b/solutions/7206.md
index 0a620df..2c286c4 100644
--- a/solutions/7206.md
+++ b/solutions/7206.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [7206](../questions/7206): *Understanding references*
+## Solution to [7206](../questions/7206.md): *Understanding references*
See code at `solutions/code/tutorialquestions/question7206`
diff --git a/solutions/735a.md b/solutions/735a.md
index 681ccf8..f8ecdc1 100644
--- a/solutions/735a.md
+++ b/solutions/735a.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [735a](../questions/735a): *Generic iterators*
+## Solution to [735a](../questions/735a.md): *Generic iterators*
See code at `solutions/code/tutorialquestions/question735a`
diff --git a/solutions/74d2.md b/solutions/74d2.md
index 0f41bab..e7b74f2 100644
--- a/solutions/74d2.md
+++ b/solutions/74d2.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [74d2](../questions/74d2): *Exceptions and inheritance (i)*
+## Solution to [74d2](../questions/74d2.md): *Exceptions and inheritance (i)*
See code at `solutions/code/tutorialquestions/question74d2`
diff --git a/solutions/7e2a.md b/solutions/7e2a.md
index 7914b0b..3998018 100644
--- a/solutions/7e2a.md
+++ b/solutions/7e2a.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [7e2a](../questions/7e2a): *Stack overflow*
+## Solution to [7e2a](../questions/7e2a.md): *Stack overflow*
See code at `solutions/code/tutorialquestions/question7e2a`
diff --git a/solutions/7ec8.md b/solutions/7ec8.md
index 44483bf..dadd02a 100644
--- a/solutions/7ec8.md
+++ b/solutions/7ec8.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [7ec8](../questions/7ec8): *Battling fighters*
+## Solution to [7ec8](../questions/7ec8.md): *Battling fighters*
See code at `solutions/code/tutorialquestions/question7ec8`
diff --git a/solutions/845d.md b/solutions/845d.md
index f343284..1e89fc3 100644
--- a/solutions/845d.md
+++ b/solutions/845d.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [845d](../questions/845d): *Books and dictionaries*
+## Solution to [845d](../questions/845d.md): *Books and dictionaries*
See code at `solutions/code/tutorialquestions/question845d`
diff --git a/solutions/85bb.md b/solutions/85bb.md
index 9dcd1cd..8e0c002 100644
--- a/solutions/85bb.md
+++ b/solutions/85bb.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [85bb](../questions/85bb): *String stack iterators*
+## Solution to [85bb](../questions/85bb.md): *String stack iterators*
See code at `solutions/code/tutorialquestions/question85bb`
diff --git a/solutions/876b.md b/solutions/876b.md
index cb51f36..893eb95 100644
--- a/solutions/876b.md
+++ b/solutions/876b.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [876b](../questions/876b): *Generics and subclasses*
+## Solution to [876b](../questions/876b.md): *Generics and subclasses*
See code at `solutions/code/tutorialquestions/question876b`
diff --git a/solutions/888a.md b/solutions/888a.md
new file mode 100755
index 0000000..b1447c1
--- /dev/null
+++ b/solutions/888a.md
@@ -0,0 +1,11 @@
+[Back to questions](../README.md)
+
+## Solution to [888a](../questions/888a.md): *Generic methods with streams*
+
+See code at `solutions/code/tutorialquestions/question888a`
+
+Compare your solution with the code in the sample solution.
+
+The main thing to understand about the code for these questions is that generic type information for a method (e.g. of the form ``) comes *before* the return type of the method, and *after* any other keywords such as `static` or `public`.
+
+The sample implementation of `zip`, which uses `IntStream` and `mapToObj`, is worth studying carefully.
\ No newline at end of file
diff --git a/solutions/8a61.md b/solutions/8a61.md
index 1f40821..972cc55 100644
--- a/solutions/8a61.md
+++ b/solutions/8a61.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [8a61](../questions/8a61): *Int set*
+## Solution to [8a61](../questions/8a61.md): *Int set*
See code at `solutions/code/tutorialquestions/question8a61`
diff --git a/solutions/8d24.md b/solutions/8d24.md
index a2b910a..ef923b2 100644
--- a/solutions/8d24.md
+++ b/solutions/8d24.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [8d24](../questions/8d24): *Lucky battling fighters*
+## Solution to [8d24](../questions/8d24.md): *Lucky battling fighters*
See code at `solutions/code/tutorialquestions/question8d24`
diff --git a/solutions/8f65.md b/solutions/8f65.md
index 9370b98..d5e74c5 100644
--- a/solutions/8f65.md
+++ b/solutions/8f65.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [8f65](../questions/8f65): *Lucky battling fighters with inheritance*
+## Solution to [8f65](../questions/8f65.md): *Lucky battling fighters with inheritance*
See code at `solutions/code/tutorialquestions/question8f65`
diff --git a/solutions/937d.md b/solutions/937d.md
index a5e4d16..1e80921 100644
--- a/solutions/937d.md
+++ b/solutions/937d.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [937d](../questions/937d): *Flawed rectangle*
+## Solution to [937d](../questions/937d.md): *Flawed rectangle*
See code at `solutions/code/tutorialquestions/question937d`
diff --git a/solutions/96df.md b/solutions/96df.md
index 23a6c72..5acefbc 100644
--- a/solutions/96df.md
+++ b/solutions/96df.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [96df](../questions/96df): *Tree nodes*
+## Solution to [96df](../questions/96df.md): *Tree nodes*
See code at `solutions/code/tutorialquestions/question96df`
diff --git a/solutions/98e3.md b/solutions/98e3.md
index 204dd91..ee81d60 100755
--- a/solutions/98e3.md
+++ b/solutions/98e3.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [98e3](../questions/98e3): *... 1 4 2 1 4 2 1 ...*
+## Solution to [98e3](../questions/98e3.md): *... 1 4 2 1 4 2 1 ...*
See code at `solutions/code/tutorialquestions/question98e3`
diff --git a/solutions/9a9b.md b/solutions/9a9b.md
index 5ba2248..b8ad4c8 100644
--- a/solutions/9a9b.md
+++ b/solutions/9a9b.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [9a9b](../questions/9a9b): *Transposing tunes*
+## Solution to [9a9b](../questions/9a9b.md): *Transposing tunes*
See code at `solutions/code/tutorialquestions/question9a9b`
diff --git a/solutions/a22c.md b/solutions/a22c.md
index 59fdc07..daea6ff 100644
--- a/solutions/a22c.md
+++ b/solutions/a22c.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [a22c](../questions/a22c): *No duplicate email addresses*
+## Solution to [a22c](../questions/a22c.md): *No duplicate email addresses*
See code at `solutions/code/tutorialquestions/questiona22c`
diff --git a/solutions/a6e7.md b/solutions/a6e7.md
index 0aab49c..4cd7b46 100644
--- a/solutions/a6e7.md
+++ b/solutions/a6e7.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [a6e7](../questions/a6e7): *Int set iterators*
+## Solution to [a6e7](../questions/a6e7.md): *Int set iterators*
See code at `solutions/code/tutorialquestions/questiona6e7`
diff --git a/solutions/aa68.md b/solutions/aa68.md
index 0fa9576..351d81b 100644
--- a/solutions/aa68.md
+++ b/solutions/aa68.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [aa68](../questions/aa68): *Symmetric equality testing*
+## Solution to [aa68](../questions/aa68.md): *Symmetric equality testing*
See code at `solutions/code/tutorialquestions/questionaa68`
@@ -33,7 +33,6 @@ public boolean canEqual(Object that) {
Note that it is *usually* bad practice to override a method and make no reference to `super`. This is an exception (though note that if the overridden version of `canEqual` returns *true*, the superclass version would be guaranteed to do the same).
-The implementation of `equals` in `ColouredPoint` is now adapted analogously to how `equals` was adapted for `Point`: we check that the `ColouredPoint` on which `equals` is being invoked `canEqual` the incoming object. We then cast the incoming object to a `ColouredPoint`, and test whether this `ColouredPoint` `canEqual` the `ColouredPoint` on which `equals` is being invoked. Finally, we test superclass equality, and compare on the `colour` fields, as before.
+The implementation of `equals` in `ColouredPoint` is now adapted analogously to how `equals` was adapted for `Point`: we check that the `ColouredPoint` on which `equals` is being invoked `canEqual` the incoming object. We then cast the incoming object to a `ColouredPoint` and test superclass equality. Due to the way we implemented `equals` in `Point`, this superclass call will test whether this `ColouredPoint` `canEqual` the `ColouredPoint` on which `equals` is being invoked. Finally, we compare on the `colour` fields, as before.
The crucial missing property that `canEqual` has added is that if we compare a plain old `Point` with a `ColouredPoint` we are *guaranteed* to get the result `false`. Thus the asymmetry identified in [question 5235](../questions/5235.md) cannot occur.
-
diff --git a/solutions/b33f.md b/solutions/b33f.md
new file mode 100755
index 0000000..4eccb20
--- /dev/null
+++ b/solutions/b33f.md
@@ -0,0 +1,11 @@
+[Back to questions](../README.md)
+
+## Solution to [b33f](../questions/b33f.md): *Logging using a functional interface*
+
+See code at `solutions/code/tutorialquestions/questionb33f`
+
+Compare the sample source code for this question with your solution.
+
+Notice the use of the `ifPresent` method of `Optional` in my implementation of `FileInspector`. This takes a *consumer* -- a void function whose argument is the element type of the optional, and can be used to perform an operation on the element of the optional if it exists.
+
+Notice also the use of the `@FunctionalInterface` annotation on the `Logger` and `StringInspector` interfaces. This annotation makes it illegal to add any more non-static, non-default methods to these interfaces, ensuring that it will always be possible to implement the interfaces concisely using lambdas. Think about why adding static or default methods is OK.
\ No newline at end of file
diff --git a/solutions/b401.md b/solutions/b401.md
index 78195cf..0da9358 100644
--- a/solutions/b401.md
+++ b/solutions/b401.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [b401](../questions/b401): *Generic sets*
+## Solution to [b401](../questions/b401.md): *Generic sets*
See code at `solutions/code/tutorialquestions/questionb401`
diff --git a/solutions/b4a5.md b/solutions/b4a5.md
index 57c4321..a23d866 100644
--- a/solutions/b4a5.md
+++ b/solutions/b4a5.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [b4a5](../questions/b4a5): *Observing the garbage collector*
+## Solution to [b4a5](../questions/b4a5.md): *Observing the garbage collector*
See code at `solutions/code/tutorialquestions/questionb4a5`
diff --git a/solutions/bdb4.md b/solutions/bdb4.md
index 7c80da3..a587e45 100644
--- a/solutions/bdb4.md
+++ b/solutions/bdb4.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [bdb4](../questions/bdb4): *Flawed house*
+## Solution to [bdb4](../questions/bdb4.md): *Flawed house*
See code at `solutions/code/tutorialquestions/questionbdb4`
diff --git a/solutions/bec2.md b/solutions/bec2.md
index 97bcfaf..1f448ec 100644
--- a/solutions/bec2.md
+++ b/solutions/bec2.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [bec2](../questions/bec2): *Music collection*
+## Solution to [bec2](../questions/bec2.md): *Music collection*
See code at `solutions/code/tutorialquestions/questionbec2`
diff --git a/solutions/c2b8.md b/solutions/c2b8.md
index ff7f955..9b18a73 100644
--- a/solutions/c2b8.md
+++ b/solutions/c2b8.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [c2b8](../questions/c2b8): *Irresponsible rectangle*
+## Solution to [c2b8](../questions/c2b8.md): *Irresponsible rectangle*
See code at `solutions/code/tutorialquestions/questionc2b8`
diff --git a/solutions/c822.md b/solutions/c822.md
index b3085bd..c3ed1e0 100644
--- a/solutions/c822.md
+++ b/solutions/c822.md
@@ -1,6 +1,6 @@
[Back to questions](../README.md)
-## Solution to [c822](../questions/c822): *Problems cloning tree nodes*
+## Solution to [c822](../questions/c822.md): *Problems cloning tree nodes*
See code at `solutions/code/tutorialquestions/questionc822`
diff --git a/solutions/code/tutorialquestions/question014e/RandomIntegers.java b/solutions/code/tutorialquestions/question014e/RandomIntegers.java
index b268cf7..81d66a8 100755
--- a/solutions/code/tutorialquestions/question014e/RandomIntegers.java
+++ b/solutions/code/tutorialquestions/question014e/RandomIntegers.java
@@ -45,7 +45,7 @@ public static void main(String[] args) {
}
}
- System.out.println("");
+ System.out.println();
System.out.println("I had to generate " + numbersGenerated
+ " random numbers between 0 and " + (maxNumber - 1)
+ " to have produced all such numbers at least once.");
diff --git a/solutions/code/tutorialquestions/question0c21/instanceofsolution/PropertyCollection.java b/solutions/code/tutorialquestions/question0c21/instanceofsolution/PropertyCollection.java
index 4e18ceb..e9cddf8 100755
--- a/solutions/code/tutorialquestions/question0c21/instanceofsolution/PropertyCollection.java
+++ b/solutions/code/tutorialquestions/question0c21/instanceofsolution/PropertyCollection.java
@@ -6,10 +6,10 @@
public class PropertyCollection {
- private Set properties;
+ private final Set properties;
public PropertyCollection() {
- properties = new HashSet();
+ properties = new HashSet<>();
}
public void addProperty(Property property) {
@@ -17,7 +17,7 @@ public void addProperty(Property property) {
}
public Set getHouses() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p instanceof House) {
result.add((House) p);
@@ -27,7 +27,7 @@ public Set getHouses() {
}
public Set getBungalows() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p instanceof Bungalow) {
result.add((Bungalow) p);
@@ -37,7 +37,7 @@ public Set getBungalows() {
}
public Set getFlats() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p instanceof Flat) {
result.add((Flat) p);
@@ -47,7 +47,7 @@ public Set getFlats() {
}
public Set getMaisonettes() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p instanceof Maisonette) {
result.add((Maisonette) p);
@@ -57,7 +57,7 @@ public Set getMaisonettes() {
}
public Set getTerracedHouses() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p instanceof TerracedHouse) {
result.add((TerracedHouse) p);
diff --git a/solutions/code/tutorialquestions/question0c21/methodssolution/PropertyCollection.java b/solutions/code/tutorialquestions/question0c21/methodssolution/PropertyCollection.java
index 321fa52..0158543 100755
--- a/solutions/code/tutorialquestions/question0c21/methodssolution/PropertyCollection.java
+++ b/solutions/code/tutorialquestions/question0c21/methodssolution/PropertyCollection.java
@@ -6,10 +6,10 @@
public class PropertyCollection {
- private Set properties;
+ private final Set properties;
public PropertyCollection() {
- properties = new HashSet();
+ properties = new HashSet<>();
}
public void addProperty(Property property) {
@@ -17,7 +17,7 @@ public void addProperty(Property property) {
}
public Set getHouses() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p.isHouse()) {
result.add((House) p);
@@ -27,7 +27,7 @@ public Set getHouses() {
}
public Set getBungalows() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p.isBungalow()) {
result.add((Bungalow) p);
@@ -37,7 +37,7 @@ public Set getBungalows() {
}
public Set getFlats() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p.isFlat()) {
result.add((Flat) p);
@@ -47,7 +47,7 @@ public Set getFlats() {
}
public Set getMaisonettes() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p.isMaisonette()) {
result.add((Maisonette) p);
@@ -57,7 +57,7 @@ public Set getMaisonettes() {
}
public Set getTerracedHouses() {
- Set result = new HashSet();
+ Set result = new HashSet<>();
for (Property p : properties) {
if (p.isTerraced()) {
result.add((TerracedHouse) p);
diff --git a/solutions/code/tutorialquestions/question1171/original/GraphNode.java b/solutions/code/tutorialquestions/question1171/original/GraphNode.java
index fc44b7d..340bda2 100755
--- a/solutions/code/tutorialquestions/question1171/original/GraphNode.java
+++ b/solutions/code/tutorialquestions/question1171/original/GraphNode.java
@@ -6,10 +6,10 @@
public class GraphNode {
private E key;
- private List> successors;
+ private final List> successors;
public GraphNode() {
- successors = new ArrayList>();
+ successors = new ArrayList<>();
}
public int getNumberOfSuccessors() {
diff --git a/solutions/code/tutorialquestions/question1171/solution/Demo.java b/solutions/code/tutorialquestions/question1171/solution/Demo.java
index 2a9ff73..087597d 100755
--- a/solutions/code/tutorialquestions/question1171/solution/Demo.java
+++ b/solutions/code/tutorialquestions/question1171/solution/Demo.java
@@ -6,11 +6,11 @@ public class Demo {
public static void main(String[] args) {
// Make some nodes
- GraphNode original = new GraphNode();
+ GraphNode original = new GraphNode<>();
original.setKey("Hello");
- GraphNode child1 = new GraphNode();
+ GraphNode child1 = new GraphNode<>();
child1.setKey("Child 1");
- GraphNode child2 = new GraphNode();
+ GraphNode child2 = new GraphNode<>();
child1.setKey("Child 2");
// Join them up
@@ -20,7 +20,7 @@ public static void main(String[] args) {
child2.addSuccessor(original); // Creates a cycle
// Clone original
- GraphNode clone = (GraphNode) original.clone();
+ GraphNode clone = original.clone();
// Check that the clone uses distinct nodes
assert original != clone;
diff --git a/solutions/code/tutorialquestions/question1171/solution/GraphNode.java b/solutions/code/tutorialquestions/question1171/solution/GraphNode.java
index c086897..ddd7e92 100755
--- a/solutions/code/tutorialquestions/question1171/solution/GraphNode.java
+++ b/solutions/code/tutorialquestions/question1171/solution/GraphNode.java
@@ -13,7 +13,7 @@ public class GraphNode implements Cloneable {
private List> successors;
public GraphNode() {
- successors = new ArrayList>();
+ successors = new ArrayList<>();
}
public int getNumberOfSuccessors() {
@@ -39,9 +39,9 @@ public void setKey(E key) {
@Override
public GraphNode clone() {
- Map, GraphNode> oldToNew = new HashMap, GraphNode>();
+ Map, GraphNode> oldToNew = new HashMap<>();
- Deque> stack = new ArrayDeque>();
+ Deque> stack = new ArrayDeque<>();
stack.push(this);
@@ -70,7 +70,7 @@ public GraphNode clone() {
private GraphNode internalClone() {
try {
GraphNode result = (GraphNode) super.clone();
- result.successors = new ArrayList>();
+ result.successors = new ArrayList<>();
return result;
} catch (CloneNotSupportedException ex) {
// This should never be thrown, because
diff --git a/solutions/code/tutorialquestions/question11e2/Example.java b/solutions/code/tutorialquestions/question11e2/Example.java
new file mode 100755
index 0000000..5641b78
--- /dev/null
+++ b/solutions/code/tutorialquestions/question11e2/Example.java
@@ -0,0 +1,56 @@
+package tutorialquestions.question11e2;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public class Example {
+
+ public static > Optional
+ getSmallestCollection(List collections) {
+ return collections
+ .stream()
+ .reduce((collection1, collection2) ->
+ collection1.size() < collection2.size() ? collection1 : collection2);
+ }
+
+ public static void main(String[] args) {
+ final Set s1 = Set.of(1, 2, 3, 4);
+ final Set s2 = Set.of(1, 2);
+ final Set s3 = Set.of(1, 2, 3, 4);
+ final Set s4 = Set.of(3, 4);
+
+ final List l1 = new ArrayList<>(List.of("alpha", "beta", "gamma"));
+ final List l2 = new ArrayList<>(List.of("ay", "bee", "cee"));
+ final List l3 = new ArrayList<>(List.of("mouse", "fox", "owl", "snake",
+ "gruffalo"));
+ final List l4 = new ArrayList<>(List.of("dog", "cat", "frog", "bird", "witch",
+ "broomstick"));
+ final List l5 = new ArrayList<>(List.of("dragon", "pox"));
+ final List l6 = new ArrayList<>(List.of("dragon", "pox"));
+
+ final List> listOfSetsOfIntegers = List.of(s1, s2, s3, s4);
+ final List> listOfListsOfStrings = List.of(l1, l2, l3, l4, l5, l6);
+
+ // Think about why the following would not compile:
+ // final List> listOfCollections = List.of(
+ // s1, s2, s3, s4, l1, l2, l3, l4, l5);
+ final List> listOfCollections = List.of(s1, s2, s3, s4, l1, l2, l3, l4, l5);
+
+ final Optional> smallestSet = getSmallestCollection(listOfSetsOfIntegers);
+ assert smallestSet.isPresent();
+ assert smallestSet.get().size() == 2;
+
+ final Optional> smallestList = getSmallestCollection(listOfListsOfStrings);
+ assert smallestList.isPresent();
+ // Think about why '==' would *not* be OK here.
+ assert smallestList.get().equals(l5);
+
+ // Think about why the following would not compile:
+ // final Optional> smallestCollection = getSmallestCollection(listOfCollections);
+
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question1486/StringStackArray.java b/solutions/code/tutorialquestions/question1486/StringStackArray.java
index 6c04c29..b9cd00a 100755
--- a/solutions/code/tutorialquestions/question1486/StringStackArray.java
+++ b/solutions/code/tutorialquestions/question1486/StringStackArray.java
@@ -4,7 +4,7 @@ public class StringStackArray implements StringStack {
private static final int STACK_LIMIT = 100;
- private String[] elements;
+ private final String[] elements;
private int stackPointer;
public StringStackArray() {
diff --git a/solutions/code/tutorialquestions/question1486/StringStackList.java b/solutions/code/tutorialquestions/question1486/StringStackList.java
index b557a1c..6daa9e8 100755
--- a/solutions/code/tutorialquestions/question1486/StringStackList.java
+++ b/solutions/code/tutorialquestions/question1486/StringStackList.java
@@ -5,10 +5,10 @@
public class StringStackList implements StringStack {
- private List elements;
+ private final List elements;
public StringStackList() {
- elements = new ArrayList();
+ elements = new ArrayList<>();
}
@Override
diff --git a/solutions/code/tutorialquestions/question17b1/afterdefault/A.java b/solutions/code/tutorialquestions/question17b1/afterdefault/A.java
new file mode 100755
index 0000000..869ed5a
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/afterdefault/A.java
@@ -0,0 +1,29 @@
+package tutorialquestions.question17b1.afterdefault;
+
+public class A implements I {
+ @Override
+ public int foo() {
+ return 0;
+ }
+
+ @Override
+ public int foo(int x) {
+ return 0;
+ }
+
+ @Override
+ public int bar(int x) {
+ return 0;
+ }
+
+ public int foobar() {
+ return 42;
+ }
+
+ // This method had to be renamed to avoid a clash with I.foobar, which had the same name
+ // and parameter types but a different return type.
+ public void foobarOriginal(int x) {
+ System.out.println(x);
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/afterdefault/B.java b/solutions/code/tutorialquestions/question17b1/afterdefault/B.java
new file mode 100755
index 0000000..e4c12d7
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/afterdefault/B.java
@@ -0,0 +1,26 @@
+package tutorialquestions.question17b1.afterdefault;
+
+public class B implements I, J {
+ @Override
+ public int foo() {
+ return 0;
+ }
+
+ @Override
+ public int foo(int x) {
+ return 0;
+ }
+
+ @Override
+ public int bar(int x) {
+ return 0;
+ }
+
+ // This method had to be provided to avoid ambiguity between which of I.foobar and J.foobar
+ // would be called if foobar were invoked on an instance of B. This implementation chooses
+ // the version of foobar from I.
+ @Override
+ public int foobar(int x) {
+ return I.super.foobar(x);
+ }
+}
diff --git a/solutions/code/tutorialquestions/question17b1/afterdefault/C.java b/solutions/code/tutorialquestions/question17b1/afterdefault/C.java
new file mode 100755
index 0000000..10fe815
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/afterdefault/C.java
@@ -0,0 +1,43 @@
+package tutorialquestions.question17b1.afterdefault;
+
+import java.io.IOException;
+
+public class C implements K {
+ @Override
+ public void baz() {
+
+ }
+
+ @Override
+ public int foo() {
+ return 0;
+ }
+
+ @Override
+ public int foo(int x) {
+ return 0;
+ }
+
+ @Override
+ public int bar(int x) {
+ return 0;
+ }
+
+ // This method had to be renamed because the default zero-argument foobar from I does not
+ // specify that it might throw an exception, and thus clients that invoke that method would not
+ // expect a checked exception to be thrown. It is illegal to increase the extent to which
+ // exceptions are thrown when overriding or implementing a method from a superclass or
+ // interface.
+ public int foobarOriginal() throws IOException {
+ throw new IOException();
+ }
+
+ // This method had to be renamed because I and J provide implicitly *public* default
+ // implementations of foobar with this signature. It is illegal to decrease the visibility of
+ // a method from a superclass or interface when overriding or implementing it; this method has
+ // protected visibility.
+ protected int foobarOriginal(int x) {
+ return x;
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/afterdefault/I.java b/solutions/code/tutorialquestions/question17b1/afterdefault/I.java
new file mode 100755
index 0000000..09763a0
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/afterdefault/I.java
@@ -0,0 +1,19 @@
+package tutorialquestions.question17b1.afterdefault;
+
+public interface I {
+
+ int foo();
+
+ int foo(int x);
+
+ int bar(int x);
+
+ default int foobar() {
+ return bar(foo());
+ }
+
+ default int foobar(int x) {
+ return bar(foo(x));
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/afterdefault/J.java b/solutions/code/tutorialquestions/question17b1/afterdefault/J.java
new file mode 100755
index 0000000..e4e0c3d
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/afterdefault/J.java
@@ -0,0 +1,13 @@
+package tutorialquestions.question17b1.afterdefault;
+
+public interface J {
+
+ int foo(int x);
+
+ int bar(int y);
+
+ default int foobar(int x) {
+ return bar(foo(x));
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/afterdefault/K.java b/solutions/code/tutorialquestions/question17b1/afterdefault/K.java
new file mode 100755
index 0000000..604da9f
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/afterdefault/K.java
@@ -0,0 +1,11 @@
+package tutorialquestions.question17b1.afterdefault;
+
+public interface K extends I, J {
+
+ void baz();
+
+ default int foobar(int x) {
+ return I.super.foobar(x);
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/beforedefault/A.java b/solutions/code/tutorialquestions/question17b1/beforedefault/A.java
new file mode 100755
index 0000000..fbaad9d
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/beforedefault/A.java
@@ -0,0 +1,27 @@
+package tutorialquestions.question17b1.beforedefault;
+
+public class A implements I {
+ @Override
+ public int foo() {
+ return 0;
+ }
+
+ @Override
+ public int foo(int x) {
+ return 0;
+ }
+
+ @Override
+ public int bar(int x) {
+ return 0;
+ }
+
+ public int foobar() {
+ return 42;
+ }
+
+ public void foobar(int x) {
+ System.out.println(x);
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/beforedefault/B.java b/solutions/code/tutorialquestions/question17b1/beforedefault/B.java
new file mode 100755
index 0000000..f253dc7
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/beforedefault/B.java
@@ -0,0 +1,19 @@
+package tutorialquestions.question17b1.beforedefault;
+
+public class B implements I, J {
+ @Override
+ public int foo() {
+ return 0;
+ }
+
+ @Override
+ public int foo(int x) {
+ return 0;
+ }
+
+ @Override
+ public int bar(int x) {
+ return 0;
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/beforedefault/C.java b/solutions/code/tutorialquestions/question17b1/beforedefault/C.java
new file mode 100755
index 0000000..55fad00
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/beforedefault/C.java
@@ -0,0 +1,34 @@
+package tutorialquestions.question17b1.beforedefault;
+
+import java.io.IOException;
+
+public class C implements K {
+ @Override
+ public void baz() {
+
+ }
+
+ @Override
+ public int foo() {
+ return 0;
+ }
+
+ @Override
+ public int foo(int x) {
+ return 0;
+ }
+
+ @Override
+ public int bar(int x) {
+ return 0;
+ }
+
+ public int foobar() throws IOException {
+ throw new IOException();
+ }
+
+ protected int foobar(int x) {
+ return x;
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/beforedefault/I.java b/solutions/code/tutorialquestions/question17b1/beforedefault/I.java
new file mode 100755
index 0000000..dc6134a
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/beforedefault/I.java
@@ -0,0 +1,11 @@
+package tutorialquestions.question17b1.beforedefault;
+
+public interface I {
+
+ int foo();
+
+ int foo(int x);
+
+ int bar(int x);
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/beforedefault/J.java b/solutions/code/tutorialquestions/question17b1/beforedefault/J.java
new file mode 100755
index 0000000..9f2ddbe
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/beforedefault/J.java
@@ -0,0 +1,9 @@
+package tutorialquestions.question17b1.beforedefault;
+
+public interface J {
+
+ int foo(int x);
+
+ int bar(int y);
+
+}
diff --git a/solutions/code/tutorialquestions/question17b1/beforedefault/K.java b/solutions/code/tutorialquestions/question17b1/beforedefault/K.java
new file mode 100755
index 0000000..7e4c254
--- /dev/null
+++ b/solutions/code/tutorialquestions/question17b1/beforedefault/K.java
@@ -0,0 +1,7 @@
+package tutorialquestions.question17b1.beforedefault;
+
+public interface K extends I, J {
+
+ void baz();
+
+}
diff --git a/solutions/code/tutorialquestions/question1ae9/objectpool/Demo.java b/solutions/code/tutorialquestions/question1ae9/objectpool/Demo.java
index 612599d..db1c944 100755
--- a/solutions/code/tutorialquestions/question1ae9/objectpool/Demo.java
+++ b/solutions/code/tutorialquestions/question1ae9/objectpool/Demo.java
@@ -7,7 +7,7 @@ public class Demo {
public static void main(String[] args) {
- List pointList = new ArrayList();
+ List pointList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
pointList.add(Point.makePoint(0, 0, 0));
diff --git a/solutions/code/tutorialquestions/question1ae9/objectpool/Point.java b/solutions/code/tutorialquestions/question1ae9/objectpool/Point.java
index 74944e3..fd6ba45 100755
--- a/solutions/code/tutorialquestions/question1ae9/objectpool/Point.java
+++ b/solutions/code/tutorialquestions/question1ae9/objectpool/Point.java
@@ -9,7 +9,7 @@ public class Point {
private final int coordY;
private final int coordZ;
- private static Map pool = new HashMap();
+ private static final Map pool = new HashMap<>();
private Point(int coordX, int coordY, int coordZ) {
this.coordX = coordX;
@@ -42,9 +42,9 @@ public boolean equals(Object that) {
@Override
public int hashCode() {
- return new Integer(coordX).hashCode()
- ^ new Integer(coordY).hashCode()
- ^ new Integer(coordZ).hashCode();
+ return Integer.valueOf(coordX).hashCode()
+ ^ Integer.valueOf(coordY).hashCode()
+ ^ Integer.valueOf(coordZ).hashCode();
}
}
diff --git a/solutions/code/tutorialquestions/question1ae9/original/Demo.java b/solutions/code/tutorialquestions/question1ae9/original/Demo.java
index 21222f2..4975409 100755
--- a/solutions/code/tutorialquestions/question1ae9/original/Demo.java
+++ b/solutions/code/tutorialquestions/question1ae9/original/Demo.java
@@ -7,7 +7,7 @@ public class Demo {
public static void main(String[] args) {
- List pointList = new ArrayList();
+ List pointList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
pointList.add(new Point(0, 0, 0));
diff --git a/solutions/code/tutorialquestions/question1ae9/original/Point.java b/solutions/code/tutorialquestions/question1ae9/original/Point.java
index 5d813c8..ed63139 100755
--- a/solutions/code/tutorialquestions/question1ae9/original/Point.java
+++ b/solutions/code/tutorialquestions/question1ae9/original/Point.java
@@ -27,9 +27,9 @@ public boolean equals(Object that) {
@Override
public int hashCode() {
- return new Integer(coordX).hashCode()
- ^ new Integer(coordY).hashCode()
- ^ new Integer(coordZ).hashCode();
+ return Integer.valueOf(coordX).hashCode()
+ ^ Integer.valueOf(coordY).hashCode()
+ ^ Integer.valueOf(coordZ).hashCode();
}
}
diff --git a/solutions/code/tutorialquestions/question1aeb/NumberManipulator.java b/solutions/code/tutorialquestions/question1aeb/NumberManipulator.java
index da28a9d..96b1ca0 100755
--- a/solutions/code/tutorialquestions/question1aeb/NumberManipulator.java
+++ b/solutions/code/tutorialquestions/question1aeb/NumberManipulator.java
@@ -12,7 +12,7 @@ public class NumberManipulator {
public static Set readNumbers(int count, NumberParser parser)
throws IOException {
- Set result = new HashSet();
+ Set result = new HashSet<>();
BufferedReader br = new BufferedReader(
new InputStreamReader(
diff --git a/solutions/code/tutorialquestions/question290b/weakreferences/Point.java b/solutions/code/tutorialquestions/question290b/weakreferences/Point.java
index 087a37e..fd0a791 100755
--- a/solutions/code/tutorialquestions/question290b/weakreferences/Point.java
+++ b/solutions/code/tutorialquestions/question290b/weakreferences/Point.java
@@ -10,7 +10,7 @@ public class Point {
private final int coordY;
private final int coordZ;
- private static Map> pool = new WeakHashMap<>();
+ private static final Map> pool = new WeakHashMap<>();
private Point(int coordX, int coordY, int coordZ) {
this.coordX = coordX;
@@ -27,7 +27,7 @@ public static Point makePoint(int coordX, int coordY, int coordZ) {
return pool.get(point).get();
// point will now be garbage collected
}
- pool.put(point, new WeakReference(point));
+ pool.put(point, new WeakReference<>(point));
return point;
}
@@ -38,8 +38,7 @@ public String toString() {
@Override
public boolean equals(Object that) {
- return that != null
- && that instanceof Point
+ return that instanceof Point
&& coordX == ((Point) that).coordX
&& coordY == ((Point) that).coordY
&& coordZ == ((Point) that).coordZ;
@@ -47,9 +46,9 @@ public boolean equals(Object that) {
@Override
public int hashCode() {
- return new Integer(coordX).hashCode()
- ^ new Integer(coordY).hashCode()
- ^ new Integer(coordZ).hashCode();
+ return Integer.valueOf(coordX).hashCode()
+ ^ Integer.valueOf(coordY).hashCode()
+ ^ Integer.valueOf(coordZ).hashCode();
}
/**
diff --git a/solutions/code/tutorialquestions/question2d33/ReversedOrderOfInputStack.java b/solutions/code/tutorialquestions/question2d33/ReversedOrderOfInputStack.java
index e80decb..521276a 100755
--- a/solutions/code/tutorialquestions/question2d33/ReversedOrderOfInputStack.java
+++ b/solutions/code/tutorialquestions/question2d33/ReversedOrderOfInputStack.java
@@ -13,9 +13,9 @@ public class ReversedOrderOfInputStack {
*/
public static void main(String[] args) throws IOException {
- Deque dq = new ArrayDeque<>();
+ final Deque dq = new ArrayDeque<>();
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+ final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
diff --git a/solutions/code/tutorialquestions/question2ffc/Demo.java b/solutions/code/tutorialquestions/question2ffc/Demo.java
index 7b46d1e..0a4675f 100755
--- a/solutions/code/tutorialquestions/question2ffc/Demo.java
+++ b/solutions/code/tutorialquestions/question2ffc/Demo.java
@@ -10,9 +10,9 @@ public static void transferStacks(GenericStack dest, GenericStack src)
public static void main(String[] args) {
- final GenericStack first = new GenericStackArray();
+ final GenericStack first = new GenericStackArray<>();
- final GenericStack second = new GenericStackList();
+ final GenericStack second = new GenericStackList<>();
first.push("The");
first.push("quick");
diff --git a/solutions/code/tutorialquestions/question2ffc/GenericStackArray.java b/solutions/code/tutorialquestions/question2ffc/GenericStackArray.java
index e2ef302..7d8c7e6 100755
--- a/solutions/code/tutorialquestions/question2ffc/GenericStackArray.java
+++ b/solutions/code/tutorialquestions/question2ffc/GenericStackArray.java
@@ -4,7 +4,7 @@ public class GenericStackArray implements GenericStack {
private static final int STACK_LIMIT = 100;
- private E[] elements;
+ private final E[] elements;
private int stackPointer;
@SuppressWarnings("unchecked")
diff --git a/solutions/code/tutorialquestions/question2ffc/GenericStackList.java b/solutions/code/tutorialquestions/question2ffc/GenericStackList.java
index 96c10c6..68bd4bb 100755
--- a/solutions/code/tutorialquestions/question2ffc/GenericStackList.java
+++ b/solutions/code/tutorialquestions/question2ffc/GenericStackList.java
@@ -5,10 +5,10 @@
public class GenericStackList implements GenericStack {
- private List elements;
+ private final List elements;
public GenericStackList() {
- elements = new ArrayList();
+ elements = new ArrayList<>();
}
@Override
diff --git a/solutions/code/tutorialquestions/question30cd/HeapExhaustion.java b/solutions/code/tutorialquestions/question30cd/HeapExhaustion.java
index 5aa55e0..9a66331 100755
--- a/solutions/code/tutorialquestions/question30cd/HeapExhaustion.java
+++ b/solutions/code/tutorialquestions/question30cd/HeapExhaustion.java
@@ -7,11 +7,12 @@ public class HeapExhaustion {
public static void main(String[] args) {
- List infiniteList = new ArrayList();
+ final List infiniteList = new ArrayList<>();
try {
+ //noinspection InfiniteLoopStatement
while (true) {
- infiniteList.add(new Integer(0));
+ infiniteList.add(0);
}
} catch (OutOfMemoryError exception) {
System.out.println("Length of list before memory exhausted: " + infiniteList.size());
diff --git a/solutions/code/tutorialquestions/question336b/Demo.java b/solutions/code/tutorialquestions/question336b/Demo.java
new file mode 100755
index 0000000..4b781dc
--- /dev/null
+++ b/solutions/code/tutorialquestions/question336b/Demo.java
@@ -0,0 +1,59 @@
+package tutorialquestions.question336b;
+
+
+import java.util.List;
+
+public class Demo {
+
+ private static final int MEDIUM_NUMBER = 10000;
+ private static final int LARGE_NUMBER = 1000000;
+
+ public static void main(String[] args) {
+
+ final GenericSet set1 = new MemoryEfficientGenericSet<>();
+
+ final GenericSet set2 = new SpeedEfficientGenericSet<>();
+
+ for (int i = 0; i < MEDIUM_NUMBER; i++) {
+ set1.add(i);
+ set2.add(i);
+ }
+
+ final Integer[] lotsOfDuplicates = new Integer[LARGE_NUMBER];
+ for (int i = 0; i < LARGE_NUMBER; i++) {
+ // Add LARGE_NUMBER or (LARGE_NUMBER + 1) at position i of the array, depending on the parity
+ // of i.
+ lotsOfDuplicates[i] = LARGE_NUMBER + (i % 2);
+ }
+
+ set1.addAll(lotsOfDuplicates);
+ set2.addAll(lotsOfDuplicates);
+
+ for (GenericSet set : List.of(set1, set2)) {
+ if (!set.contains(LARGE_NUMBER) || !set.contains(LARGE_NUMBER + 1)) {
+ throw new AssertionError("Expected the set to contain " + LARGE_NUMBER);
+ }
+
+ if (!set.asUnmodifiableSet().contains(LARGE_NUMBER)
+ || !set.asUnmodifiableSet().contains(LARGE_NUMBER + 1)) {
+ throw new AssertionError("An unmodifiable version of the set should still contain these"
+ + " elements");
+ }
+
+ try {
+ set.asUnmodifiableSet().add(2);
+ throw new AssertionError("Attempting to add to an unmodifiable set should have failed.");
+ } catch (UnsupportedOperationException exception1) {
+ // Good - an exception should have been thrown.
+ try {
+ set.asUnmodifiableSet().addAll(lotsOfDuplicates);
+ throw new AssertionError("Attempting to add to an unmodifiable set should have failed.");
+ } catch (UnsupportedOperationException exception2) {
+ // Good - an exception should also have been thrown, due to the default implementation of
+ // 'addAll' invoking 'add'.
+ }
+ }
+ }
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question336b/GenericSet.java b/solutions/code/tutorialquestions/question336b/GenericSet.java
new file mode 100755
index 0000000..da4a8cb
--- /dev/null
+++ b/solutions/code/tutorialquestions/question336b/GenericSet.java
@@ -0,0 +1,60 @@
+package tutorialquestions.question336b;
+
+public interface GenericSet {
+
+ void add(E item);
+
+ boolean remove(E item);
+
+ boolean isEmpty();
+
+ boolean contains(E item);
+
+ // This is a vanilla method for adding every element in an array to the set, regardless of what
+ // the underlying set representation is. This might end up being very efficient for some set
+ // representations, but it is functionally correct.
+ default void addAll(E[] items) {
+ for (E item : items) {
+ add(item);
+ }
+ }
+
+ // This returns a wrapper object that implements the GenericSet interface and throws an exception
+ // if either of the mutator methods are called. For the other methods, it delegates to the
+ // original set's methods. Think about what happens if you call one of the default methods on
+ // the set returned by 'asUnmodifiableSet'.
+ default GenericSet asUnmodifiableSet() {
+
+ // This is an example of an *anonymous class* that implements the GenericSet interface. An
+ // alternative to using an anonymous class would be to have a generic class called
+ // UnmodifiableWrapperSet that implements the GenericSet interface and provides this
+ // functionality.
+ return new GenericSet<>() {
+ @Override
+ public void add(E item) {
+ throw new UnsupportedOperationException("Attempt to add to an unmodifiable set.");
+ }
+
+ @Override
+ public boolean remove(E item) {
+ throw new UnsupportedOperationException("Attempt to remove from an unmodifiable set.");
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // Interesting syntax: if we just wrote "return isEmpty()" that would invoke the "isEmpty"
+ // method on the anonymous inner class, i.e. the current method, leading to infinite
+ // recursion. By writing "GenericSet.this" we ask for the outer class's version of the
+ // method to be invoked.
+ return GenericSet.this.isEmpty();
+ }
+
+ @Override
+ public boolean contains(E item) {
+ // See note above about this syntax.
+ return GenericSet.this.contains(item);
+ }
+ };
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question336b/MemoryEfficientGenericSet.java b/solutions/code/tutorialquestions/question336b/MemoryEfficientGenericSet.java
new file mode 100755
index 0000000..60b04dd
--- /dev/null
+++ b/solutions/code/tutorialquestions/question336b/MemoryEfficientGenericSet.java
@@ -0,0 +1,62 @@
+package tutorialquestions.question336b;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class MemoryEfficientGenericSet implements GenericSet {
+
+ private final List elements;
+
+ public MemoryEfficientGenericSet() {
+ elements = new ArrayList<>();
+ }
+
+ @Override
+ public void add(E item) {
+ if (elements.contains(item)) {
+ return;
+ }
+ elements.add(item);
+ }
+
+ @Override
+ public boolean contains(E item) {
+ return elements.contains(item);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return elements.isEmpty();
+ }
+
+ @Override
+ public boolean remove(E item) {
+ return elements.remove(item);
+ }
+
+ // This overrides the default implementation of addAll to do something more efficient for
+ // this kind of set.
+ @Override
+ public void addAll(E[] items) {
+ // Keep track of all the elements that are known to belong to this set, storing them as a
+ // HashSet, which supports efficient lookup.
+ final Set knownElements = new HashSet<>(elements);
+ // Now go through all of the items to be added to our memory-efficient set.
+ for (E item : items) {
+ // Efficiently check whether we know this element already exists. If it does,
+ // do nothing.
+ if (!knownElements.contains(item)) {
+ // The element is new, so:
+ // (1) Add it directly to the end of 'elements' - don't go through our 'add' method, which
+ // would check (inefficiently) whether the element is already present.
+ elements.add(item);
+ // (2) Record the fact that we have seen this element, so that if it occurs in 'elements'
+ // again we skip it.
+ knownElements.add(item);
+ }
+ }
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question336b/SpeedEfficientGenericSet.java b/solutions/code/tutorialquestions/question336b/SpeedEfficientGenericSet.java
new file mode 100755
index 0000000..0b4799e
--- /dev/null
+++ b/solutions/code/tutorialquestions/question336b/SpeedEfficientGenericSet.java
@@ -0,0 +1,34 @@
+package tutorialquestions.question336b;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class SpeedEfficientGenericSet implements GenericSet {
+
+ private final Set elements;
+
+ public SpeedEfficientGenericSet() {
+ elements = new HashSet<>();
+ }
+
+ @Override
+ public void add(E item) {
+ elements.add(item);
+ }
+
+ @Override
+ public boolean contains(E item) {
+ return elements.contains(item);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return elements.isEmpty();
+ }
+
+ @Override
+ public boolean remove(E item) {
+ return elements.remove(item);
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question4c70/Lottery.java b/solutions/code/tutorialquestions/question4c70/Lottery.java
index acb699b..fee6625 100755
--- a/solutions/code/tutorialquestions/question4c70/Lottery.java
+++ b/solutions/code/tutorialquestions/question4c70/Lottery.java
@@ -10,11 +10,11 @@ public class Lottery {
private static boolean numberAlreadyChosen(int[] numbers,
int numChosenSoFar, int candidate) {
+
+ assert numChosenSoFar < numbers.length;
for (int i = 0; i < numChosenSoFar; i++) {
- assert numChosenSoFar < numbers.length;
-
if (numbers[i] == candidate) {
return true;
@@ -60,4 +60,4 @@ public static void main(String[] args) {
}
-}
\ No newline at end of file
+}
diff --git a/solutions/code/tutorialquestions/question5566/StringStackArray.java b/solutions/code/tutorialquestions/question5566/StringStackArray.java
index 5353180..78de1f7 100755
--- a/solutions/code/tutorialquestions/question5566/StringStackArray.java
+++ b/solutions/code/tutorialquestions/question5566/StringStackArray.java
@@ -4,7 +4,7 @@ public class StringStackArray implements StringStack {
private static final int STACK_LIMIT = 100;
- private String[] elements;
+ private final String[] elements;
private int stackPointer;
public StringStackArray() {
diff --git a/solutions/code/tutorialquestions/question5566/StringStackList.java b/solutions/code/tutorialquestions/question5566/StringStackList.java
index 05267fa..cdebd98 100755
--- a/solutions/code/tutorialquestions/question5566/StringStackList.java
+++ b/solutions/code/tutorialquestions/question5566/StringStackList.java
@@ -5,10 +5,10 @@
public class StringStackList implements StringStack {
- private List elements;
+ private final List elements;
public StringStackList() {
- elements = new ArrayList();
+ elements = new ArrayList<>();
}
@Override
diff --git a/solutions/code/tutorialquestions/question5981/baddesign1/Shape.java b/solutions/code/tutorialquestions/question5981/baddesign1/Shape.java
index 479fa87..38e6811 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign1/Shape.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign1/Shape.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign1;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public class Shape {
// Represents semi-major axis if shape is an Ellipse,
@@ -19,7 +16,7 @@ public class Shape {
// Ignored if shape is a Circle or Square
private int second;
- private ShapeType type;
+ private final ShapeType type;
public Shape(int first, int second, ShapeType type) {
this.first = first;
diff --git a/solutions/code/tutorialquestions/question5981/baddesign1/ShapeType.java b/solutions/code/tutorialquestions/question5981/baddesign1/ShapeType.java
index 718e364..b1ed972 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign1/ShapeType.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign1/ShapeType.java
@@ -1,5 +1,5 @@
package tutorialquestions.question5981.baddesign1;
public enum ShapeType {
- Ellipse, Circle, Rectangle, Square;
+ Ellipse, Circle, Rectangle, Square
}
diff --git a/solutions/code/tutorialquestions/question5981/baddesign2/Circle.java b/solutions/code/tutorialquestions/question5981/baddesign2/Circle.java
index b2065c8..5c9b58a 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign2/Circle.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign2/Circle.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign2;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public class Circle extends Ellipse {
public Circle(int radius) {
diff --git a/solutions/code/tutorialquestions/question5981/baddesign2/Ellipse.java b/solutions/code/tutorialquestions/question5981/baddesign2/Ellipse.java
index 2dabd21..ac10702 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign2/Ellipse.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign2/Ellipse.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign2;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public class Ellipse extends Shape {
private int semiMajorAxis;
diff --git a/solutions/code/tutorialquestions/question5981/baddesign2/Rectangle.java b/solutions/code/tutorialquestions/question5981/baddesign2/Rectangle.java
index 90ef1fb..02236da 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign2/Rectangle.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign2/Rectangle.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign2;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public class Rectangle extends Shape {
private int width;
diff --git a/solutions/code/tutorialquestions/question5981/baddesign2/Shape.java b/solutions/code/tutorialquestions/question5981/baddesign2/Shape.java
index 5a5168e..2ed00b2 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign2/Shape.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign2/Shape.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign2;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public abstract class Shape {
}
diff --git a/solutions/code/tutorialquestions/question5981/baddesign2/Square.java b/solutions/code/tutorialquestions/question5981/baddesign2/Square.java
index dc56ba3..b5e158b 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign2/Square.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign2/Square.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign2;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public class Square extends Rectangle {
public Square(int side) {
diff --git a/solutions/code/tutorialquestions/question5981/baddesign3/Ellipse.java b/solutions/code/tutorialquestions/question5981/baddesign3/Ellipse.java
index 474db4c..6ba28e5 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign3/Ellipse.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign3/Ellipse.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign3;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public class Ellipse extends Shape {
public Ellipse(int semiMajorAxis, int semiMinorAxis) {
diff --git a/solutions/code/tutorialquestions/question5981/baddesign3/Rectangle.java b/solutions/code/tutorialquestions/question5981/baddesign3/Rectangle.java
index ebf46e7..ea2f28f 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign3/Rectangle.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign3/Rectangle.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign3;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public class Rectangle extends Shape {
public Rectangle(int width, int height) {
diff --git a/solutions/code/tutorialquestions/question5981/baddesign3/Shape.java b/solutions/code/tutorialquestions/question5981/baddesign3/Shape.java
index f13f2a1..57b4428 100755
--- a/solutions/code/tutorialquestions/question5981/baddesign3/Shape.java
+++ b/solutions/code/tutorialquestions/question5981/baddesign3/Shape.java
@@ -1,12 +1,9 @@
-/***************************************************/
-/* This class illustrates a POOR DESIGN
- * for the shapes question
- *
- * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
- ***************************************************/
-
package tutorialquestions.question5981.baddesign3;
+/**
+ * This class illustrates a POOR DESIGN for the shapes question.
+ * DO NOT CONFUSE THIS WITH THE SAMPLE SOLUTION!!!
+ */
public abstract class Shape {
protected int widthOrSemimajorAxis;
diff --git a/solutions/code/tutorialquestions/question5d30/UnreliableBufferedReader.java b/solutions/code/tutorialquestions/question5d30/UnreliableBufferedReader.java
index d676417..8bfdefb 100755
--- a/solutions/code/tutorialquestions/question5d30/UnreliableBufferedReader.java
+++ b/solutions/code/tutorialquestions/question5d30/UnreliableBufferedReader.java
@@ -7,9 +7,9 @@
public class UnreliableBufferedReader extends BufferedReader {
- private double probabilityOfError;
+ private final double probabilityOfError;
- private Random generator;
+ private final Random generator;
/**
* Makes an unreliable reader from the given input stream reader with given probability of error.
diff --git a/solutions/code/tutorialquestions/question6346/AddExpr.java b/solutions/code/tutorialquestions/question6346/AddExpr.java
index 0a8d53a..1dff402 100755
--- a/solutions/code/tutorialquestions/question6346/AddExpr.java
+++ b/solutions/code/tutorialquestions/question6346/AddExpr.java
@@ -2,8 +2,8 @@
public class AddExpr implements Expr {
- private Expr lhs;
- private Expr rhs;
+ private final Expr lhs;
+ private final Expr rhs;
public AddExpr(Expr lhs, Expr rhs) {
this.lhs = lhs;
diff --git a/solutions/code/tutorialquestions/question6346/FactExpr.java b/solutions/code/tutorialquestions/question6346/FactExpr.java
index d788c57..6a8c03e 100755
--- a/solutions/code/tutorialquestions/question6346/FactExpr.java
+++ b/solutions/code/tutorialquestions/question6346/FactExpr.java
@@ -2,7 +2,7 @@
public class FactExpr implements Expr {
- private Expr operand;
+ private final Expr operand;
public FactExpr(Expr operand) {
this.operand = operand;
diff --git a/solutions/code/tutorialquestions/question6346/LiteralExpr.java b/solutions/code/tutorialquestions/question6346/LiteralExpr.java
index 615242d..3c075cd 100755
--- a/solutions/code/tutorialquestions/question6346/LiteralExpr.java
+++ b/solutions/code/tutorialquestions/question6346/LiteralExpr.java
@@ -2,7 +2,7 @@
public class LiteralExpr implements Expr {
- private int value;
+ private final int value;
public LiteralExpr(int value) {
this.value = value;
diff --git a/solutions/code/tutorialquestions/question6346/MulExpr.java b/solutions/code/tutorialquestions/question6346/MulExpr.java
index fed29d1..bfb752c 100755
--- a/solutions/code/tutorialquestions/question6346/MulExpr.java
+++ b/solutions/code/tutorialquestions/question6346/MulExpr.java
@@ -2,8 +2,8 @@
public class MulExpr implements Expr {
- private Expr lhs;
- private Expr rhs;
+ private final Expr lhs;
+ private final Expr rhs;
public MulExpr(Expr lhs, Expr rhs) {
this.lhs = lhs;
diff --git a/solutions/code/tutorialquestions/question68e6/Example.java b/solutions/code/tutorialquestions/question68e6/Example.java
new file mode 100755
index 0000000..1732fb8
--- /dev/null
+++ b/solutions/code/tutorialquestions/question68e6/Example.java
@@ -0,0 +1,85 @@
+package tutorialquestions.question68e6;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class Example {
+
+ static List concatenate(List> lists) {
+ return lists
+ .stream()
+ .reduce(Collections.emptyList(),
+ (first, second) -> {
+ List result = new ArrayList<>(first);
+ result.addAll(second);
+ return result;
+ });
+ }
+
+ static int findMin(List numbers) {
+ return numbers
+ .stream()
+ .reduce(Integer.MAX_VALUE, Math::min);
+ }
+
+ static int findMinOrZero(List numbers) {
+ return numbers
+ .stream()
+ .reduce(Math::min)
+ .orElse(0);
+ }
+
+ static int findMax(List numbers) {
+ return numbers
+ .stream()
+ .reduce(Integer.MIN_VALUE, (first, second) -> Math.max(first, second));
+ }
+
+ static int findMaxOrZero(List numbers) {
+ return numbers
+ .stream()
+ .reduce((first, second) -> Math.max(first, second))
+ .orElse(0);
+ }
+
+ static int findMinOfMaxes(List> listOfLists) {
+ return findMin(listOfLists
+ .stream()
+ .map(Example::findMax)
+ .collect(Collectors.toList()));
+ }
+
+ public static void main(String[] args) {
+ final List list1 = List.of(1, 2, 3, 4, 5, 9);
+ final List list2 = List.of(1, 10, 100, 1000, 10000);
+ final List list3 = List.of(6, 7, 8);
+ final List> listOfLists = List.of(list1, list2, list3);
+
+ final List allIntegers = concatenate(listOfLists);
+ final int maxList1 = findMax(list1);
+ final int minList2 = findMin(list2);
+ final int maxEmpty = findMax(Collections.emptyList());
+ final int minEmpty = findMin(Collections.emptyList());
+ final int maxOrZeroEmpty = findMinOrZero(Collections.emptyList());
+ final int minOrZeroEmpty = findMaxOrZero(Collections.emptyList());
+ final int minOfMaxes = findMinOfMaxes(listOfLists);
+ final int minOfMaxesEmpty = findMinOfMaxes(Collections.emptyList());
+ final int minOfMaxesListOfEmptyLists = findMinOfMaxes(
+ List.of(Collections.emptyList(), Collections.emptyList()));
+
+ System.out.println("Concatenation of all the integer lists: " + allIntegers);
+ System.out.println("Max of list 1: " + maxList1);
+ System.out.println("Min of list 2: " + minList2);
+ System.out.println("Max of empty list: " + maxEmpty);
+ System.out.println("Min of empty list: " + minEmpty);
+ System.out.println("Max-or-zero of empty list: " + maxOrZeroEmpty);
+ System.out.println("Min-or-zero of empty list: " + minOrZeroEmpty);
+ System.out.println("Min of maxes: " + minOfMaxes);
+ System.out.println("Min of maxes of empty list: " + minOfMaxesEmpty);
+ System.out.println("Min of maxes of list of empty lists: " + minOfMaxesListOfEmptyLists);
+
+ }
+
+}
diff --git a/solutions/code/tutorialquestions/question7041/TreeNode.java b/solutions/code/tutorialquestions/question7041/TreeNode.java
index 39e49f9..043984c 100755
--- a/solutions/code/tutorialquestions/question7041/TreeNode.java
+++ b/solutions/code/tutorialquestions/question7041/TreeNode.java
@@ -3,7 +3,7 @@
public class TreeNode {
private E key;
- private TreeNode[] children;
+ private final TreeNode[] children;
@SuppressWarnings("unchecked")
public TreeNode(int numberOfChildren) {
@@ -31,7 +31,7 @@ public void setKey(E key) {
}
public TreeNode clone() {
- TreeNode result = new TreeNode(getNumberOfChildren());
+ TreeNode result = new TreeNode<>(getNumberOfChildren());
result.setKey(getKey());
for (int i = 0; i < getNumberOfChildren(); i++) {
result.setChild(i, getChild(i).clone());
diff --git a/solutions/code/tutorialquestions/question710c/hashcode/Point.java b/solutions/code/tutorialquestions/question710c/hashcode/Point.java
index 72950e1..873c503 100755
--- a/solutions/code/tutorialquestions/question710c/hashcode/Point.java
+++ b/solutions/code/tutorialquestions/question710c/hashcode/Point.java
@@ -49,8 +49,8 @@ public boolean equals(Object that) {
@Override
public int hashCode() {
- return new Double(coordX).hashCode() ^ new Double(coordY).hashCode()
- ^ new Double(coordZ).hashCode();
+ return Double.valueOf(coordX).hashCode() ^ Double.valueOf(coordY).hashCode()
+ ^ Double.valueOf(coordZ).hashCode();
}
diff --git a/solutions/code/tutorialquestions/question710c/hashcode/PointHashCodeDemo.java b/solutions/code/tutorialquestions/question710c/hashcode/PointHashCodeDemo.java
index dab2c6d..6d315e9 100755
--- a/solutions/code/tutorialquestions/question710c/hashcode/PointHashCodeDemo.java
+++ b/solutions/code/tutorialquestions/question710c/hashcode/PointHashCodeDemo.java
@@ -17,7 +17,7 @@ public static void main(String[] args) {
final Point firstColoured = new ColouredPoint(1.2, 2.3, 3.4, Colour.RED);
final Point secondColoured = new ColouredPoint(1.2, 2.3, 3.4, Colour.RED);
- Set pointSet = new HashSet