Class 9 — Wednesday, September 23
Sections 1 and 3 run on the projector — keyboards down, and section 3 wants a prediction in the Journal before anything runs. Sections 2, 4 and 5 are in your own A4 repo, in pairs.
- An array that cannot grow
- Your matrices in a list
remove(1)— predict first- Removing while walking
- A list that refuses
- Swap the constructor
- The same name twice
- A window on a list
Sections 1 to 4 are the class. Sections 5 to 8 go past anything you have been asked for — take them in order if you get there.
1. An array that cannot grow
Keyboards down.
Three names into an array built for two, on purpose:
String[] names = new String[2];
names[0] = "Ada";
names[1] = "Alan";
names[2] = "Grace";Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:
Index 2 out of bounds for length 2
An array has the size it was born with. An ArrayList is an array that grows:
List<String> names = new ArrayList<>();
names.add("Ada");
names.add("Alan");
names.add("Grace");
System.out.println(names.size() + " " + names.get(2) + " " + names.contains("Alan"));
names.remove("Alan");
System.out.println(names);3 Grace true
[Ada, Grace]
Five methods, and you will use all five today: add, get, size, contains, remove. contains and remove find what you ask for with equals — the same equals you wrote for Matrix.
List is the promise and ArrayList is one class that keeps it, the way Set was the promise on Monday and HashSet and TreeSet were two classes that kept it. Section 6 is what that buys you.
2. Your matrices in a list
Keyboards up. Pairs. In your A4 repo, any main you like.
You need java.util.List, java.util.ArrayList and java.util.Collections.
List<Matrix> ms = new ArrayList<>();
ms.add(new Matrix(new double[][] {{3, 4}}));
ms.add(new Matrix(new double[][] {{1, 1.6}}));
ms.add(new Matrix(new double[][] {{1, 1}, {1, 1}}));
ms.add(new Matrix(new double[][] {{5, 12}}));
System.out.println(ms.size() + " " + ms.contains(new Matrix(new double[][] {{1, 1.6}})));Predict the second value before you run it. Then sort the list two ways — the order Matrix has, and the order your BySize has — and print the magnitudes after the first sort and the shapes after the second.
Collections.sort(ms);
ms.sort(new BySize());Done when you get
4 true
2.6 4.0 7.0 17.0
1x2 1x2 1x2 2x2
contains said true for a matrix you never put in. It found an equal one, because it asked equals, and your equals compares numbers rather than addresses. Nothing about Matrix changed this week. A list of your matrices can be searched and sorted because of two methods you wrote before it existed.
3. remove(1)
Keyboards down. Prediction before it runs.
Two lists, the same three numbers, one call each:
List<Integer> xs = new ArrayList<>(List.of(1, 5, 7));
xs.remove(1);
System.out.println(xs);
List<Integer> ys = new ArrayList<>(List.of(1, 5, 7));
ys.remove(Integer.valueOf(1));
System.out.println(ys);Write both lines in the Journal before it runs. A guess counts as a line.
What do the two lines print?
[1, 7]
[5, 7]
remove(1) did not remove the 1. It removed position 1, which held the 5.
List has two remove methods — remove(int index) and remove(Object o) — and the compiler picked one by the type of what you passed. A bare 1 is an int, so it is an index. Integer.valueOf(1) is an object, so it is a thing to look for.
Nothing warned you. Both calls compiled, both ran, and one of them did something you did not ask for. The compiler chose the method by the argument’s type, not by what you meant — and you have seen that before, in Class 5, in an equals that took a Fraction.
4. Removing while walking
Keyboards up. Pairs.
Four names. Take out the ones that start with Al:
List<String> names = new ArrayList<>(List.of("Ada", "Alan", "Grace", "Alonzo"));
for (String n : names) {
if (n.startsWith("Al")) {
names.remove(n);
}
}
System.out.println(names);Predict, then run.
Exception in thread "main" java.util.ConcurrentModificationException
The loop is walking the list, and you changed the list under it. A list refuses to carry on rather than guess where it now is.
The list will let you remove while walking if you ask it to do the walking. An Iterator is the cursor the for loop was using all along:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (it.next().startsWith("Al")) {
it.remove();
}
}
System.out.println(names);Done when you get
[Ada, Grace]
and one of you can say why the first version could not have known what to do next.
5. A list that refuses
You have been writing List.of(...) since Monday. Try to add to one:
List<String> fixed = List.of("Ada", "Alan");
System.out.println(fixed.get(0));
fixed.add("Grace");Predict, then run.
Ada
Exception in thread "main" java.lang.UnsupportedOperationException
List.of builds a list that cannot change — the same decision Matrix made with its final fields, taken by a list. get works. add, remove and set throw.
When you want a list you can change, copy it: new ArrayList<>(List.of(...)), which is what sections 3 and 4 did.
6. Swap the constructor
This is the one to get to.
LinkedList keeps the same promise as ArrayList, so every line above works with one word changed:
List<Integer> xs = new LinkedList<>();Put a hundred thousand numbers in each kind, then walk each one with get(i) and time it. Predict which is slower, and roughly by how much.
for (int i = 0; i < xs.size(); i++) {
sum += xs.get(i);
}ArrayList get(i) over 100000: 1 ms
LinkedList get(i) over 100000: 2874 ms
An ArrayList has an array inside, and get(i) is one jump. A LinkedList is a chain of nodes, and get(i) walks i links from the front every time you ask — so a loop over get(i) walks the whole chain over and over.
Same interface, same answers, three thousand times the cost. The promise says what you get back. It says nothing about how long it takes.
7. The same name twice
A set holds each thing once. A list does not. Predict all four lines.
List<String> names = new ArrayList<>();
names.add("Ada");
names.add("Alan");
names.add("Ada");
System.out.println(names.size() + " " + names.indexOf("Ada") + " " + names.lastIndexOf("Ada"));
names.remove("Ada");
System.out.println(names);
Set<String> once = new HashSet<>(List.of("Ada", "Alan", "Ada"));
System.out.println(once.size());3 0 2
[Alan, Ada]
2
remove("Ada") took out the first one and left the other. A list is positions, so “the same name” can be in two of them, and remove is a promise about one position.
The set was handed three names and kept two. Monday’s rule, from the other side: a set decides what counts as the same, and a list never asks.
Then, in your A4 repo: add the same Matrix object to a List<Matrix> twice, and a different object with the same numbers a third time. Predict size(), indexOf of the different object, and what remove of it takes out.
8. A window on a list
subList(from, to) hands you part of a list. Predict the three lines before you run them. The second and third are the ones people get wrong.
List<Integer> xs = new ArrayList<>(List.of(1, 2, 3, 4, 5));
List<Integer> mid = xs.subList(1, 4);
System.out.println(mid);
mid.set(0, 99);
System.out.println(xs);
mid.clear();
System.out.println(xs);[2, 3, 4]
[1, 99, 3, 4, 5]
[1, 5]
subList did not copy anything. It is a window onto the same list: change the window and the list changes, clear the window and three elements are gone from the list.
You met this shape in A3, when a constructor that kept the caller’s array let the caller change the matrix afterwards. When you want your own copy, say so: new ArrayList<>(xs.subList(1, 4)).
Done when you can say which of the eight sections today handed you a copy and which handed you a window.