Chapter 14 Generics and Collections Flashcards

1
Q

Functional interfaces

A
  1. Supplier<T>
    T get()
  2. Consumer<T>
    void accept(T t)
  3. BiConsumer<T, U>
    void accept(T t, U u)
  4. Predicate<T>
    boolean test(T t)
  5. BiPredicate<T, U>
    boolean test(T t, U u)
  6. Function<T, R>
    R apply(T t)
  7. BiFunction<T, U, R>
    R apply(T t, U u)
  8. UnaryOperator<T>
    static <T> UnaryOperator<T> identity()
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Using Method References

A
@FunctionalInterface
public interface LearnToSpeak {
    void speak(String sound);
}

public class DuckHelper {
    public static void teacher(String name, LearnToSpeak trainer) {
        // exercise patience
        trainer.speak(name);
    }
}

This code implements the functional interface using a lambda:

LearnToSpeak learner = s -> System.out.println(s);

A method reference lets us remove that redundancy and instead write this:

LearnToSpeak learner = System.out::println;

The :: operator tells Java to call the println() method later.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

four types of method references

A

There are four formats for method references:

  • Static methods
  • Instance methods on a particular instance
  • Instance methods on a parameter to be determined at runtime
  • Constructors
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

> [!NOTE]
Remember that :: is like a lambda, and it is used for deferred execution with a functional interface.
A method reference and a lambda behave the same way at runtime.
You can pretend the compiler turns your method references into lambdas for you.

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

CALLING STATIC METHODS

A
14: Consumer<List<Integer>> methodRef = Collections::sort;
  • Java is inferring information from the context.
  • In this case, we said that we were declaring a Consumer, which takes only one parameter.
  • Java looks for a method that matches that description.
  • If it can’t find it or it finds multiple ones that could match multiple methods, then the compiler will report an error.
  • The latter is sometimes called an ambiguous type error.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

CALLING INSTANCE METHODS ON A PARTICULAR OBJECT

A

Predicate is a functional interface that takes one parameter and returns a boolean.

18: var str = "abc";
19: Predicate<String> methodRef = str::startsWith;
20: Predicate<String> lambda = s -> str.startsWith(s);

A method reference doesn’t have to take any parameters.

var random = new Random();
Supplier<Integer> methodRef = random::nextInt;
Supplier<Integer> lambda = () -> random.nextInt();
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

CALLING INSTANCE METHODS ON A PARAMETER

A
14: Consumer<List<Integer>> methodRef = Collections::sort;
15: Consumer<List<Integer>> lambda = x -> Collections.sort(x);
  • Line 23 says the method that we want to call is declared in String.
  • It looks like a static method, but it isn’t.
  • Instead, Java knows that isEmpty() is an instance method that does not take any parameters.
  • Java uses the parameter supplied at runtime as the instance on which the method is called.

BiPredicate, which takes two parameters and returns a boolean.

26: BiPredicate<String, String> methodRef = String::startsWith;
27: BiPredicate<String, String> lambda = (s, p) -> s.startsWith(p);
  • Since the functional interface takes two parameters, Java has to figure out what they represent.
  • The first one will always be the instance of the object for instance methods.
  • Any others are to be method parameters.
  • Remember that line 26 may look like a static method, but it is really a method reference declaring that the instance of the object will be specified later.
  • Line 27 shows some of the power of a method reference. We were able to replace two lambda parameters this time.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

CALLING CONSTRUCTORS

A

A constructor reference is a special type of method reference that uses new instead of a method, and it instantiates an object.

30: Supplier<List<String>> methodRef = ArrayList::new;
31: Supplier<List<String>> lambda = () -> new ArrayList();
32: Function<Integer, List<String>> methodRef = ArrayList::new;
33: Function<Integer, List<String>> lambda = x -> new ArrayList(x);

Java sees that we are passing an Integer parameter and calls the constructor of ArrayList that takes a parameter.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

REVIEWING METHOD REFERENCES

A

Method references

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

NUMBER OF PARAMETERS IN A METHOD REFERENCE

A
public class Penguin {
    public static Integer countBabies(Penguin… cuties) {
        return cuties.length;
    }
}
10: Supplier<Integer> methodRef1 = Penguin::countBabies;
11: Supplier<Integer> lambda1 = () -> Penguin.countBabies();
12:
13: Function<Penguin, Integer> methodRef2 = Penguin::countBabies;
14: Function<Penguin, Integer> lambda2 = (x) -> Penguin.countBabies(x);
15:
16: BiFunction<Penguin, Penguin, Integer> methodRef3 = Penguin::countBabies;
17: BiFunction<Penguin, Penguin, Integer> lambda3 = (x, y) -> Penguin.countBabies(x, y);
  • Lines 10 and 11 do not take any parameters because the functional interface is a Supplier.
  • Lines 13 and 14 take one parameter.
  • Lines 16 and 17 take two parameters.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Using Wrapper Classes

A
  • With autoboxing, the compiler automatically converts a primitive to the corresponding wrapper.
  • Unsurprisingly, unboxing is the process in which the compiler automatically converts a wrapper class back to a primitive.

Wrapper classes initializing

  1. Boolean.valueOf(true)
  2. Byte.valueOf((byte) 1)
  3. Short.valueOf((short) 1)
  4. Integer.valueOf(1)
  5. Long.valueOf(1)
  6. Float.valueOf((float) 1.0)
  7. Double.valueOf(1.0)
  8. Character.valueOf(‘c’)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Can you spot the autoboxing and unboxing in this example?

12: Integer pounds = 120;
13: Character letter = "robot".charAt(0);
14: char r = letter;
A
  • Line 12 is an example of autoboxing as the int primitive is autoboxed into an Integer object.
  • Line 13 demonstrates that autoboxing can involve methods. The charAt() method returns a primitive char. It is then autoboxed into the wrapper object Character.
  • Finally, line 14 shows an example of unboxing. The Character object is unboxed into a primitive char.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

This innocuous-looking code throws an exception:

15: var heights = new ArrayList<Integer>();
16: heights.add(null);
17: int h = heights.get(0); // NullPointerException
A
  • On line 16, we add a null to the list. This is legal because a null reference can be assigned to any reference variable.
  • On line 17, we try to unbox that null to an int primitive. This is a problem. Java tries to get the int value of null. Since calling any method on null gives a NullPointerException, that is just what we get.
  • Be careful when you see null in relation to autoboxing.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

WRAPPER CLASSES AND NULL

What do you think this code outputs?

23: List<Integer> numbers = new ArrayList<Integer>();
24: numbers.add(1);
25: numbers.add(Integer.valueOf(3));
26: numbers.add(Integer.valueOf(5));
27: numbers.remove(1);
28: numbers.remove(Integer.valueOf(5));
29: System.out.println(numbers);
A

> [!NOTE]
one advantage of a wrapper class over a primitive is that it can hold a null value.

It actually outputs [1].

  • On lines 24 through 26, we add three Integer objects to numbers, numbers contains [1, 3, 5].
  • On line 27, Java sees a matching signature for int, so it doesn’t need to autobox the call to the method.
  • Now numbers contains [1, 5].
  • Line 28 calls the other remove() method, and it
    removes the matching object, which leaves us with just [1].
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Using the Diamond Operator

A
  • The diamond operator, <>, was added to the language.
  • The diamond operator is a shorthand notation that allows you to omit the generic type from the right side of a statement when the type can be inferred.
  • It is called the diamond operator because <> looks like a diamond.
List<Integer> list = new ArrayList<>();
Map<String,Integer> map = new HashMap<>();
Map<Long,List<Integer>> mapOfLists = new HashMap<>();

The diamond operator cannot be used as the type in a variable declaration.

List<> list = new ArrayList<Integer>(); // DOES NOT COMPILE
Map<> map = new HashMap<String, Integer>(); // DOES NOT COMPILE
class InvalidUse {
void use(List<> data) {} // DOES NOT COMPILE
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Do you think these two statements compile and are equivalent?

var list = new ArrayList<Integer>();
var list = new ArrayList<>();
A
  • The first one creates an ArrayList<Integer>
  • The second one creates an ArrayList<Object>
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Using Lists Sets Maps and Queues

A
  • A collection is a group of objects contained in a single object.
  • The Java Collections Framework is a set of classes in java.util for storing collections.
  • There are four main interfaces in the Java Collections Framework.
    • List: A list is an ordered collection of elements that allows duplicate entries. Elements in a list can be accessed by an int index.
    • Set: A set is a collection that does not allow duplicate entries.
    • Queue: A queue is a collection that orders its elements in a specific order for processing. A typical queue processes its elements in a first-in, first-out order, but other orderings are possible.
    • Map: A map is a collection that maps keys to values, with no duplicate keys allowed. The elements in a map are key/value pairs.

Map doesn’t implement the Collection interface.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

COMMON COLLECTIONS METHODS

A
  1. inserts a new element into the Collection and returns whether it was successful.
    boolean add(E element)
  2. removes a single matching value in the Collection and returns whether it was successful.
    boolean remove(Object object)
  3. The isEmpty() and size() methods look at how many elements are in the Collection.
    boolean isEmpty()
    int size()
  4. The clear() method provides an easy way to discard all elements of the Collection.
    void clear()
  5. The contains() method checks whether a certain value is in the Collection.
    boolean contains(Object object)
  6. The removeIf() method removes all elements that match a condition.
    boolean removeIf(Predicate<? super E> filter)
  7. Looping through a Collection.
    void forEach(Consumer<? super T> action)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

add()

A

inserts a new element into the Collection and returns whether it was successful.
The method signatures are as follows:
boolean add(E element)

A List allows duplicates, making the return value true each time.
A Set does not allow duplicates,
will return false if tried to add a duplicate

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

remove()

A

removes a single matching value in the Collection and returns whether it was successful.
The method signatures are as follows:
boolean remove(Object object)

the boolean return value tells us whether a match was removed.
Remember that there are overloaded remove() methods.
One takes the element to remove.
The other takes the index of the element to remove.
calling remove() on a List with an int uses the index, an index that doesn’t exist will throw an exception. For example, birds.remove(100); throws an IndexOutOfBoundsException.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

DELETING WHILE LOOPING

A

Java does not allow removing elements from a list while using the enhanced for loop.

Collection<String> birds = new ArrayList<>();
birds.add("hawk");
birds.add("hawk");
birds.add("hawk");
for (String bird : birds) // ConcurrentModificationException
    birds.remove(bird);
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

isEmpty() and size()

A

The isEmpty() and size() methods look at how many elements are in the Collection.
The method signatures are as follows:
boolean isEmpty()
int size()

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

clear()

A

The clear() method provides an easy way to discard all elements of the Collection. The method signature is as follows:
void clear()

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

contains()

A

The contains() method checks whether a certain value is in the Collection.
The method signature is as follows:
boolean contains(Object object)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

removeIf()

A

The removeIf() method removes all elements that match a condition.
The method signature looks like the following:
boolean removeIf(Predicate<? super E> filter)

examples:

list.removeIf(s -> s.startsWith("A"));

set.removeIf(String::isEmpty); // s -> s.isEmpty()
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

forEach()

A

Looping through a Collection.
The method signature is as follows:
void forEach(Consumer<? super T> action)

examples:

cats.forEach(System.out::println);
cats.forEach(c -> System.out.println(c));
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q

USING THE LIST INTERFACE

A
  • You use a list when you want an ordered collection that can contain duplicate entries.
  • Items can be retrieved and inserted at specific positions in the list based on an int index much like an array.
  • Unlike an array, though, many List implementations can change in size after they are declared.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
28
Q

Comparing List Implementations

A

ArrayList
* An ArrayList is like a resizable array. When elements are added, the ArrayList automatically grows.
* The main benefit of an ArrayList is that you can look up any element in constant time.
* Adding or removing an element is slower than accessing an element.
* This makes an ArrayList a good choice when you are reading more often than (or the same amount as) writing to the ArrayList.

LinkedList
* A LinkedList is special because it implements both List and Queue.
* It also has additional methods to facilitate adding or removing from the beginning and/or end of the list.
* The main benefits of a LinkedList are that you can access, add, and remove from the beginning and end of the list in constant time.
* The trade-off is that dealing with an arbitrary index takes linear time.
* LinkedList a good choice when you’ll be using it as Queue.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
29
Q

Creating a List with a Factory

A

Factory methods to create a List
* Arrays.asList(varargs)
Returns fixed size list backed by an array
* List.of(varargs)
Returns immutable list
* List.copyOf(collection)
Returns immutable list with copy of original collection’s values

example of these three methods.

16: String[] array = new String[] {"a", "b", "c"};
17: List<String> asList = Arrays.asList(array); // [a, b, c]
18: List<String> of = List.of(array); // [a, b, c]
19: List<String> copy = List.copyOf(asList); // [a, b, c]
20:
21: array[0] = "z";
22:
23: System.out.println(asList); // [z, b, c]
24: System.out.println(of); // [a, b, c]
25: System.out.println(copy); // [a, b, c]
26:
27: asList.set(0, "x");
28: System.out.println(Arrays.toString(array)); // [x, b, c]
29:
30: copy.add("y"); // throws UnsupportedOperationException
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
30
Q

Working with List Methods

A

List methods

  • boolean add(E element)
    Adds element to end (available on all Collection APIs)
  • void add(int index, E element)
    Adds element at index and moves the rest toward the end
  • E get(int index)
    Returns element at index
  • E remove(int index)
    Removes element at index and moves the rest toward the front
  • void replaceAll(UnaryOperator<E> op)
    Replaces each element in the list with the result of the operator
  • E set(int index, E e)
    Replaces element at index and returns original. Throws IndexOutOfBoundsException if the index is larger than the maximum one set
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
31
Q
3: List<String> list = new ArrayList<>();
4: list.add("SD"); // [SD]
5: list.add(0, "NY"); // [NY,SD]
6: list.set(1, "FL"); // [NY,FL]
7: System.out.println(list.get(0)); // NY
8: list.remove("NY"); // [FL]
9: list.remove(0); // []
10: list.set(0, "?"); // IndexOutOfBoundsException
A
  • On line 3, list starts out empty.
  • Line 4 adds an element to the end of the list.
  • Line 5 adds an element at index 0 that bumps the original index 0 to index 1. Notice how the ArrayList is now automatically one larger.
  • Line 6 replaces the element at index 1 with a new value.
  • Line 7 uses the get() method to print the element at a specific index.
  • Line 8 removes the element matching NY. Finally,
  • line 9 removes the element at index 0, and list is empty again.
  • Line 10 throws an IndexOutOfBoundsException because there are no elements in the List. Since there are no elements to replace, even index 0 isn’t allowed. If line 10 were moved up between lines 4 and 5, the call would have succeeded.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
32
Q

Now, let’s look at using the replaceAll() method. It takes a UnaryOperator that takes one parameter and returns a value of the same type.

List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.replaceAll(x -> x*2);
System.out.println(numbers); // [2, 4, 6]
A

This lambda doubles the value of each element in the list. The replaceAll() method calls the lambda on each element of the list and replaces the value at that index.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
33
Q

ITERATING THROUGH A LIST

A

enhanced for loop

for (String string: list) {
    System.out.println(string);
}

another approach

Iterator<String> iter = list.iterator();
while(iter.hasNext()) {
    String string = iter.next();
    System.out.println(string);
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
34
Q

USING THE SET INTERFACE

A
  • You use a set when you don’t want to allow duplicate entries.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
35
Q

Comparing Set Implementations

A

HashSet

  • A HashSet stores its elements in a hash table, which means the keys are a hash and the values are an Object.
  • This means that it uses the hashCode() method of the objects to retrieve them more efficiently.
  • The main benefit is that adding elements and checking whether an element is in the set both have constant time.
  • The trade-off is that you lose the order in which you inserted the elements.
  • Most of the time, you aren’t concerned with this in a set anyway, making HashSet the most common set.

TreeSet

  • A TreeSet stores its elements in a sorted tree structure.
  • The main benefit is that the set is always in sorted order.
  • The trade-off is that adding and checking whether an element exists take longer than with a HashSet, especially as the tree grows larger.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
36
Q

Working with Set Methods

A

create an immutable Set in one line or make a copy of an existing one.

Set<Character> letters = Set.of('z', 'o', 'o');

Set<Character> copy = Set.copyOf(letters);
37
Q
3: Set<Integer> set = new HashSet<>();
4: boolean b1 = set.add(66); // true
5: boolean b2 = set.add(10); // true
6: boolean b3 = set.add(66); // false
7: boolean b4 = set.add(8); // true
8: set.forEach(System.out::println);
A

This code prints three lines:

66
8
10
  • The add() methods should be straightforward.
  • They return true unless the Integer is already in the set.
  • Line 6 returns false, because we already have 66 in the set and a set must preserve uniqueness.
  • Line 8 prints the elements of the set in an arbitrary order.
  • In this case, it happens not to be sorted order, or the order in which we added the elements.
  • Remember that the equals() method is used to determine equality.
  • The hashCode() method is used to know which bucket to look in so that Java doesn’t have to look through the whole set to find out whether an object is there.
  • The best case is that hash codes are unique, and Java has to call equals() on only one object.
  • The worst case is that all implementations return the same hashCode(), and Java has to call equals() on every element of the set anyway.
38
Q
3: Set<Integer> set = new TreeSet<>();
4: boolean b1 = set.add(66); // true
5: boolean b2 = set.add(10); // true
6: boolean b3 = set.add(66); // false
7: boolean b4 = set.add(8); // true
8: set.forEach(System.out::println);
A

This time, the code prints the following:

8
10
66

The elements are printed out in their natural sorted order.

39
Q

USING THE QUEUE INTERFACE

A
  • You use a queue when elements are added and removed in a specific order.
  • Unless stated otherwise, a queue is assumed to be FIFO (first‐in, first‐out).
  • The other format is LIFO (last‐in, first‐out), which is commonly referred to as a stack.
  • In Java, though, both can be implemented with the Queue interface.
40
Q

Comparing Queue Implementations

A

LinkedList
* LinkedList is a double‐ended queue.
* A double‐ended queue is different from a regular queue in that you can insert and remove elements from both the front and back of the queue.
* The main benefit of a LinkedList is that it implements both the List and Queue interfaces.
* The trade-off is that it isn’t as efficient as a “pure” queue.

You can use the ArrayDeque class (short for double‐ended queue) if you need a more efficient queue. However, ArrayDeque is not in scope for the exam.

41
Q

Working with Queue Methods

A
  • boolean add(E e)
    Adds an element to the back of the queue and returns true or throws an exception
  • E element()
    Returns next element or throws an exception if empty queue
  • boolean offer(E e)
    Adds an element to the back of the queue and returns whether successful
  • E remove()
    Removes and returns next element or throws an exception if empty queue
  • E poll()
    Removes and returns next element or returns null if empty queue
  • E peek()
    Returns next element or returns null if empty queue
42
Q
12: Queue<Integer> queue = new LinkedList<>();
13: System.out.println(queue.offer(10)); // true
14: System.out.println(queue.offer(4)); // true
15: System.out.println(queue.peek()); // 10
16: System.out.println(queue.poll()); // 10
17: System.out.println(queue.poll()); // 4
18: System.out.println(queue.peek()); // null
A
  • Lines 13 and 14 successfully add an element to the end of the queue.
  • Some queues are limited in size, which would cause offering an element to the queue to fail. You won’t encounter a scenario like that on the exam.
  • Line 15 looks at the first element in the queue, but it does not remove it.
  • Lines 16 and 17 actually remove the elements from the queue, which results in an empty queue.
  • Line 18 tries to look at the first element of a queue, which results in null.
43
Q

USING THE MAP INTERFACE

A
  • The main thing that all Map classes have in common is that they all have keys and values.
  • Beyond that, they each offer different functionality.
44
Q

MAP.OF() AND MAP.COPYOF()

A

helper method to create a Map.

Map.of("key1", "value1", "key2", "value2");

Suppose you miscount and leave out a value.

Map.of("key1", "value1", "key2"); // INCORRECT

This code compiles but throws an error at runtime. Passing keys and values is also harder to read because you have to keep track of which parameter is which. Luckily, there is a better way. Map also provides a method that lets you supply key/value pairs.

Map.ofEntries(
Map.entry("key1", "value1"),
Map.entry("key1", "value1"));

Conveniently, Map.copyOf(map) works just like the List and Set interface copyOf() methods.

45
Q

Comparing Map Implementations

A

HashMap

  • A HashMap stores the keys in a hash table. This means that it uses the hashCode() method of the keys to retrieve their values more efficiently.
  • The main benefit is that adding elements and retrieving the element by key both have constant time.
  • The trade-off is that you lose the order in which you inserted the elements.

LinkedHashMap
not in scope for the exam.

TreeMap
* A TreeMap stores the keys in a sorted tree structure.
* The main benefit is that the keys are always in sorted order.
* Like a TreeSet, the trade-off is that adding and checking whether a key is present takes longer as the tree grows larger.

46
Q

Working with Map Methods

A

Map doesn’t extend Collection

  • void clear()
    Removes all keys and values from the map.
  • boolean containsKey(Object key)
    Returns whether key is in map.
  • boolean containsValue(Object value)
    Returns whether value is in map.
  • Set<Map.Entry<K,V>> entrySet()
    Returns a Set of key/value pairs.
  • void forEach(BiConsumer(K key, V value))
    Loop through each key/value pair.
  • V get(Object key)
    Returns the value mapped by key or null if none is mapped.
  • V getOrDefault(Object key, V defaultValue)
    Returns the value mapped by the key or the default value if none is mapped.
  • boolean isEmpty()
    Returns whether the map is empty.
  • Set<K> keySet()
    Returns set of all keys.
  • V merge(K key, V value, Function(<V, V, V> func))
    Sets value if key not set. Runs the function if the key is set to determine the new value. Removes if null.
  • V put(K key, V value)
    Adds or replaces key/value pair. Returns previous value or null.
  • V putIfAbsent(K key, V value)
    Adds value if key not present and returns null. Otherwise, returns existing value.
  • V remove(Object key)
    Removes and returns value mapped to key. Returns null if none.
  • V replace(K key, V value)
  • Replaces the value for a given key if the key is set. Returns the original value or null if none.
  • void replaceAll(BiFunction<K , V, V> func)
    Replaces each value with the results of the function.
  • int size()
    Returns the number of entries (key/value pairs) in the map.
  • Collection<V> values()
    Returns Collection of all values.
47
Q

Basic Methods

A
  • V get(Object key)
    Returns the value mapped by key or null if none is mapped.
  • Set<K> keySet()
    Returns set of all keys.
  • V put(K key, V value)
    Adds or replaces key/value pair. Returns previous value or null.
  • Collection<V> values()
    Returns Collection of all values.
Map<String, String> map = new HashMap<>();
map.put("koala", "bamboo");
map.put("lion", "meat");
map.put("giraffe", "leaf");
String food = map.get("koala"); // bamboo
for (String key: map.keySet())
System.out.print(key + ","); // koala,giraffe,lion,
Map<String, String> map = new TreeMap<>();
map.put("koala", "bamboo");
map.put("lion", "meat");
map.put("giraffe", "leaf");
String food = map.get("koala"); // bamboo
for (String key: map.keySet())
System.out.print(key + ","); // giraffe,koala,lion,

TreeMap sorts the keys as we would expect. If we were to have called values() instead of keySet(), the order of the values would correspond to the order of the keys.

System.out.println(map.contains("lion")); // DOES NOT COMPILE
System.out.println(map.containsKey("lion")); // true
System.out.println(map.containsValue("lion")); // false
System.out.println(map.size()); // 3
map.clear();
System.out.println(map.size()); // 0
System.out.println(map.isEmpty()); // true

The contains() method is on the Collection interface but not the Map interface.

48
Q

forEach() and entrySet()

A
Map<Integer, Character> map = new HashMap<>();
map.put(1, 'a');
map.put(2, 'b');
map.put(3, 'c');
map.forEach((k, v) -> System.out.println(v));
map.values().forEach(System.out::println);
map.entrySet().forEach(e -> System.out.println(e.getKey() + e.getValue()));
49
Q

getOrDefault()

A
3: Map<Character, String> map = new HashMap<>();
4: map.put('x', "spot");
5: System.out.println("X marks the " + map.get('x'));
6: System.out.println("X marks the " + map.getOrDefault('x', ""));
7: System.out.println("Y marks the " + map.get('y'));
8: System.out.println("Y marks the " + map.getOrDefault('y', ""));

This code prints the following:

X marks the spot
X marks the spot
Y marks the null
Y marks the

getOrDefault() returns the empty string we passed as a parameter.

50
Q

replace() and replaceAll()

A
21: Map<Integer, Integer> map = new HashMap<>();
22: map.put(1, 2);
23: map.put(2, 4);
24: Integer original = map.replace(2, 10); // 4
25: System.out.println(map); // {1=2, 2=10}
26: map.replaceAll((k, v) -> k + v);
27: System.out.println(map); // {1=3, 2=12}
  • Line 24 replaces the value for key 2 and returns the original value.
  • Line 26 calls a function and sets the value of each element of the map to the result of that function. In our case, we added the key and value together.
51
Q

putIfAbsent()

A

The putIfAbsent() method sets a value in the map but skips it if the value is already set to a non-null value.

Map<String, String> favorites = new HashMap<>();
favorites.put("Jenny", "Bus Tour");
favorites.put("Tom", null);
favorites.putIfAbsent("Jenny", "Tram");
favorites.putIfAbsent("Sam", "Tram");
favorites.putIfAbsent("Tom", "Tram");
System.out.println(favorites); // {Tom=Tram, Jenny=Bus Tour,
Sam=Tram}

As you can see, Jenny’s value is not updated because one was already present. Sam wasn’t there at all, so he was added. Tom was present as a key but had a null value. Therefore, he was added as well.

52
Q

merge()

A

The merge() method adds logic of what to choose.

11: BiFunction<String, String, String> mapper = (v1, v2)
12: -> v1.length()> v2.length() ? v1: v2;
13:
14: Map<String, String> favorites = new HashMap<>();
15: favorites.put("Jenny", "Bus Tour");
16: favorites.put("Tom", "Tram");
17:
18: String jenny = favorites.merge("Jenny", "Skyride", mapper);
19: String tom = favorites.merge("Tom", "Skyride", mapper);
20:
21: System.out.println(favorites); // {Tom=Skyride, Jenny=Bus Tour}
22: System.out.println(jenny); // Bus Tour
23: System.out.println(tom); // Skyride

The merge() method also has logic for what happens if null values or missing keys are involved.

BiFunction<String, String, String> mapper =
(v1, v2) -> v1.length()> v2.length() ? v1 : v2;
Map<String, String> favorites = new HashMap<>();
favorites.put("Sam", null);
favorites.merge("Tom", "Skyride", mapper);
favorites.merge("Sam", "Skyride", mapper);
System.out.println(favorites); // {Tom=Skyride, Sam=Skyride}

Notice that the mapping function isn’t called. If it were, we’d have a NullPointerException. The mapping function is used only when there are two actual values to decide between.

The final thing to know about merge() is what happens when the mapping function is called and returns null. The key is removed from the map when this happens:

BiFunction<String, String, String> mapper = (v1, v2) -> null;
Map<String, String> favorites = new HashMap<>();
favorites.put("Jenny", "Bus Tour");
favorites.put("Tom", "Bus Tour");
favorites.merge("Jenny", "Skyride", mapper);
favorites.merge("Sam", "Skyride", mapper);
System.out.println(favorites); // {Tom=Bus Tour, Sam=Skyride}
53
Q

COMPARING COLLECTION TYPES

A

Java Collections Framework types
1. List
2. Map
3. Queue
4. Set

Collection attributes
1. ArrayList
2. HashMap
3. HashSet
4. LinkedList
5. TreeMap
6. TreeSet
7.

54
Q

OLDER COLLECTIONS

A

no longer on the exam

  • Vector: Implements List. If you don’t need concurrency, use ArrayList instead.
  • Hashtable: Implements Map. If you don’t need concurrency, use HashMap instead.
  • Stack: Implements Queue. If you don’t need concurrency, use a LinkedList instead.
55
Q

Sorting Data

A

For String objects, order is defined according to the Unicode character mapping.

As far as the exam is concerned, that means numbers sort before letters, and uppercase letters sort before lowercase letters.

56
Q

CREATING A COMPARABLE CLASS

A

Comparable interface

public interface Comparable<T> {
int compareTo(T o);
}
import java.util.*;
public class Duck implements Comparable<Duck> {
    private String name;
    public Duck(String name) {
        this.name = name;
    }
    public String toString() { // use readable output
        return name;
    }
    public int compareTo(Duck d) {
        return name.compareTo(d.name); // sorts ascendingly by name
    }
    public static void main(String[] args) {
        var ducks = new ArrayList<Duck>();
        ducks.add(new Duck("Quack"));
        ducks.add(new Duck("Puddles"));
        Collections.sort(ducks); // sort by name
        System.out.println(ducks); // [Puddles, Quack]
}}
57
Q

what the compareTo() method returns

A
  • The number 0 is returned when the current object is equivalent to the argument to compareTo().
  • A negative number (less than 0) is returned when the current object is smaller than the argument to compareTo().
  • A positive number (greater than 0) is returned when the current object is larger than the argument to compareTo().
58
Q
1: public class Animal implements Comparable<Animal> {
2: private int id;
3: public int compareTo(Animal a) {
4: return id – a.id; // sorts ascending by id
5: }
6: public static void main(String[] args) {
7: var a1 = new Animal();
8: var a2 = new Animal();
9: a1.id = 5;
10: a2.id = 7;
11: System.out.println(a1.compareTo(a2)); // -2
12: System.out.println(a1.compareTo(a1)); // 0
13: System.out.println(a2.compareTo(a1)); // 2
14: } }
A

> [!NOTE]
Remember that id ‐ a.id sorts in ascending order,
and a.id ‐ id sorts in descending order.

59
Q

Casting the compareTo() Argument

A

When dealing with legacy code or code that does not use generics, the compareTo() method requires a cast since it is passed an Object.

public class LegacyDuck implements Comparable {
    private String name;
    public int compareTo(Object obj) {
        LegacyDuck d = (LegacyDuck) obj; // cast because no generics
        return name.compareTo(d.name);
    }
}

Since we don’t specify a generic type for Comparable, Java assumes that we want an Object, which means that we have to cast to LegacyDuck before accessing instance variables on it.

60
Q

Checking for null

A
public class MissingDuck implements Comparable<MissingDuck> {
    private String name;
    public int compareTo(MissingDuck quack) {
        if (quack == null)
            throw new IllegalArgumentException("Poorly formed duck!");
        if (this.name == null && quack.name == null)
            return 0;
        else if (this.name == null) return -1;
        else if (quack.name == null) return 1;
        else return name.compareTo(quack.name);
    }
}

This method throws an exception if it is passed a null MissingDuck object. What about the ordering? If the name of a duck is null, then it’s sorted first.

61
Q

Keeping compareTo() and equals() Consistent

A
  • x.equals(y) is true whenever x.compareTo(y) equals 0.
  • Similarly, x.equals(y) must be false whenever x.compareTo(y) is not 0.
62
Q

COMPARING DATA WITH A COMPARATOR

A

Sometimes you want to sort an object that did not implement Comparable, or you want to sort objects in different ways at different times.

1: import java.util.ArrayList;
2: import java.util.Collections;
3: import java.util.Comparator;

17: public static void main(String[] args) {
18: Comparator<Duck> byWeight = new Comparator<Duck>() {
19: public int compare(Duck d1, Duck d2) {
20: return d1.getWeight()-d2.getWeight();
21: }
22: };
23: var ducks = new ArrayList<Duck>();
24: ducks.add(new Duck("Quack", 7));
25: ducks.add(new Duck("Puddles", 10));
26: Collections.sort(ducks);
27: System.out.println(ducks); // [Puddles, Quack]
28: Collections.sort(ducks, byWeight);
29: System.out.println(ducks); // [Quack, Puddles]
30: }

imports java.util.Comparator

Comparable and Comparator are in different packages, namely, java.lang versus java.util, respectively. That means Comparable can be used without an import statement, while Comparator cannot.

Comparator is a functional interface since there is only one abstract method to implement. This means that we can rewrite the comparator on lines 18‐22 using a lambda expression, as shown here:

Comparator<Duck> byWeight = (d1, d2) -> d1.getWeight()-d2.getWeight();
Alternatively, we can use a method reference and a helper method to specify we want to sort by weight.
Comparator<Duck> byWeight = Comparator.comparing(Duck::getWeight);
In this example, Comparator.comparing() is a static interface method that creates a Comparator given a lambda expression or method reference. Convenient, isn’t it?

63
Q

IS COMPARABLE A FUNCTIONAL INTERFACE?

A
  • Comparable is a functional interface since it also has a single abstract method.
  • However, using a lambda for Comparable would be silly.
  • The point of Comparable is to implement it inside the object being compared.
64
Q

COMPARING COMPARABLE AND COMPARATOR

A

Comparison of Comparable and Comparator
1. Package name
Comparable: java.lang
Comparator: java.util
2. Interface must be implemented by class comparing?
Comparable: Yes
Comparator: No
3. Abstract method name
Comparable: compareTo()
Comparator: compare()
4. Number of parameters
Comparable: 1
Comparator: 2
5. Common to declare using a lambda
Comparable: No
Comparator: Yes

65
Q

COMPARING MULTIPLE FIELDS

A

If two squirrels are from the same species, we want to sort the one that weighs the least first.

public class MultiFieldComparator implements Comparator<Squirrel> {
    public int compare(Squirrel s1, Squirrel s2) {
        int result = s1.getSpecies().compareTo(s2.getSpecies());
        if (result != 0) return result;
        return s1.getWeight()-s2.getWeight();
}}
Comparator<Squirrel> c = Comparator.comparing(Squirrel::getSpecies)
.thenComparingInt(Squirrel::getWeight);

Suppose we want to sort in descending order by species.

var c = Comparator.comparing(Squirrel::getSpecies).reversed();

66
Q

z`

Helper static methods for building a Comparator

A
  1. comparing(function) : Compare by the results of a function that returns any Object (or object autoboxed into an Object).
  2. comparingDouble(function) : Compare by the results of a function that returns a double.
  3. comparingInt(function) : Compare by the results of a function that returns an int.
  4. comparingLong(function) : Compare by the results of a function that returns a long.
  5. naturalOrder() : Sort using the order specified by the Comparable implementation on the object itself.
  6. reverseOrder() : Sort using the reverse of the order specified by the Comparable implementation on the object itself.
67
Q

> [!NOTE]
You’ve probably noticed by now that we often ignore null values in checking equality and comparing objects. This works fine for the exam. In the real world, though, things aren’t so neat. You will have to decide how to handle null values or prevent them from being in your object.

A

You will have to decide how to handle null values or prevent them from being in your object.

68
Q

SORTING AND SEARCHING

A

The Collections.sort() method uses the compareTo() method to sort.

2: public class SortRabbits {
3: static class Rabbit{ int id; }
4: public static void main(String[] args) {
5: List<Rabbit> rabbits = new ArrayList<>();
6: rabbits.add(new Rabbit());
7: Collections.sort(rabbits); // DOES NOT COMPILE
8: } }

Rabbit class is not Comparable
You can fix this by passing a Comparator to sort(). Remember that a Comparator is useful when you want to specify sort order without using a compareTo() method.

2: public class SortRabbits {
3: static class Rabbit{ int id; }
4: public static void main(String[] args) {
5: List<Rabbit> rabbits = new ArrayList<>();
6: rabbits.add(new Rabbit());
7: Comparator<Rabbit> c = (r1, r2) -> r1.id - r2.id;
8: Collections.sort(rabbits, c);
9: } }

The sort() and binarySearch() methods allow you to pass in a Comparator object when you don’t want to use the natural order.

69
Q

REVIEWING BINARYSEARCH()

A

The binarySearch() method requires a sorted List.

11: List<Integer> list = Arrays.asList(6,9,1,8);
12: Collections.sort(list); // [1, 6, 8, 9]
13: System.out.println(Collections.binarySearch(list, 6)); // 1
14: System.out.println(Collections.binarySearch(list, 3)); // -2
  • Line 12 sorts the List so we can call binary search properly.
  • Line 13 prints the index at which a match is found.
  • Line 14 prints one less than the negated index of where the requested value would need to be inserted.
  • The number 3 would need to be inserted at index 1 (after the number 1 but before the number 6). Negating that gives us −1, and subtracting 1 gives us −2.
70
Q

What do you think the following outputs?

3: var names = Arrays.asList("Fluffy", "Hoppy");
4: Comparator<String> c = Comparator.reverseOrder();
5: var index = Collections.binarySearch(names, "Hoppy", c);
6: System.out.println(index);
A

The correct answer is ‐1.

  • Before you panic, you don’t need to know that the answer is ‐1.
  • You do need to know that the answer is not defined.
  • Line 3 creates a list, [Fluffy, Hoppy]. This list happens to be sorted in ascending order.
  • Line 4 creates a Comparator that reverses the natural order.
  • Line 5 requests a binary search in descending order.
  • Since the list is in ascending order, we don’t meet the precondition for doing a search.
71
Q

Rabbit that does not implement Comparable, we try to add it to a TreeSet.

2: public class UseTreeSet {
3: static class Rabbit{ int id; }
4: public static void main(String[] args) {
5: Set<Duck> ducks = new TreeSet<>();
6: ducks.add(new Duck("Puddles"));
7:
8: Set<Rabbit> rabbits = new TreeSet<>();
9: rabbits.add(new Rabbit()); // ClassCastException
10: } }
A
  • Line 6 is fine. Duck does implement Comparable. TreeSet is able to sort it into the proper position in the set.
  • Line 9 is a problem. When TreeSet tries to sort it, Java discovers the fact that Rabbit does not implement Comparable. Java throws an exception that looks like this:
Exception in thread "main" java.lang.ClassCastException:
class Duck cannot be cast to class java.lang.Comparable

It may seem weird for this exception to be thrown when the first object is added to the set. After all, there is nothing to compare yet. Java works this way for consistency.

tell collections that require sorting that you want to use a specific Comparator, for example:

8: Set<Rabbit> rabbits = new TreeSet<>((r1, r2) -> r1.id-r2.id);
9: rabbits.add(new Rabbit());
72
Q

Working with Generics

14: static void printNames(List list) {
15: for (int i = 0; i < list.size(); i++) {
16: String name = (String) list.get(i); // ClassCastException
17: System.out.println(name);
18: }
19: }
20: public static void main(String[] args) {
21: List names = new ArrayList();
22: names.add(new StringBuilder("Webby"));
23: printNames(names);
24: }
A
  • This code throws a ClassCastException.
  • Line 22 adds a StringBuilder to list.
  • This is legal because a nongeneric list can contain anything.
  • However, line 16 is written to expect a specific class to be in there. It casts to a String, reflecting this assumption.
  • Since the assumption is incorrect, the code throws a ClassCastException that java.lang .StringBuilder cannot be cast to java.lang.String.
  • Generics fix this by allowing you to write and use parameterized types. You specify that you want an ArrayList of String objects. Now the compiler has enough information to prevent you from causing this problem in the first place.
List<String> names = new ArrayList<String>();
names.add(new StringBuilder("Webby")); // DOES NOT COMPILE

Getting a compiler error is good. You’ll know right away that something is wrong rather than hoping to discover it later.

73
Q

GENERIC CLASSES

A
  • You can introduce generics into your own classes.
  • The syntax for introducing a generic is to declare a formal type parameter in angle brackets.
  • following class named Crate has a generic type variable declared after the name of the class.
public class Crate<T> {
private T contents;
public T emptyCrate() {
return contents;
}
public void packCrate(T contents) {
this.contents = contents;
}
}

The generic type T is available anywhere within the Crate class. When you instantiate the class, you tell the compiler what T should be for that particular instance.

74
Q

NAMING CONVENTIONS FOR GENERICS

A

A type parameter can be named anything you want. The convention is to use single uppercase letters to make it obvious that they aren’t real class names. The following are common letters to use:

  • E for an element
  • K for a map key
  • V for a map value
  • N for a number
  • T for a generic data type
  • S, U, V, and so forth for multiple generic types
75
Q

Generic classes aren’t limited to having a single type parameter. This class shows two generic parameters.

public class SizeLimitedCrate<T, U> {
private T contents;
private U sizeLimit;
public SizeLimitedCrate(T contents, U sizeLimit) {
this.contents = contents;
this.sizeLimit = sizeLimit;
} }
A

T represents the type that we are putting in the crate. U represents the unit that we are using to measure the maximum size for the crate. To use this generic class, we can write the following:

Elephant elephant = new Elephant();
Integer numPounds = 15_000;
SizeLimitedCrate<Elephant, Integer> c1
= new SizeLimiteCrate<>(elephant, numPounds);

Here we specify that the type is Elephant, and the unit is Integer. We also throw in a reminder that numeric literals can contain underscores.

76
Q

WHAT IS TYPE ERASURE?

A

Specifying a generic type allows the compiler to enforce proper use of the generic type.

Behind the scenes, the compiler replaces all references to T in Crate with Object.

This process of removing the generics syntax from your code is referred to as type erasure. Type erasure allows your code to be compatible with older versions of Java that do not contain generics.

The compiler adds the relevant casts for your code to work with this type of erased class.

77
Q

GENERIC INTERFACES

A

Just like a class, an interface can declare a formal type parameter.

public interface Shippable<T> {
void ship(T t);
}

There are three ways a class can approach implementing this interface.

This lets it declare the ship() method with a Robot parameter.

class ShippableRobotCrate implements Shippable<Robot> {
public void ship(Robot t) { }
}

The next way is to create a generic class. The following concrete class allows the caller to specify the type of the generic:

class ShippableAbstractCrate<U> implements Shippable<U> {
public void ship(U t) { }
}

In this example, the type parameter could have been named anything, including T. We used U in the example so that it isn’t confusing as to what T refers to. The exam won’t mind trying to confuse you by using the same type parameter name.

78
Q

RAW TYPES

A

The final way is to not use generics at all. This is the old way of writing code. It generates a compiler warning about Shippable being a raw type, but it does compile. Here the ship() method has an Object parameter since the generic type is not defined:

class ShippableCrate implements Shippable {
public void ship(Object t) { }
}
79
Q

WHAT YOU CAN’T DO WITH GENERIC TYPES

A

There are some limitations on what you can do with a generic type. These aren’t on the exam, but it will be helpful to refer to this scenario when you are writing practice programs and run into one of these situations. Most of the limitations are due to type erasure. Oracle refers to types whose information is fully available at runtime as reifiable. Reifiable types can do anything that Java allows. Nonreifiable types have some limitations. Here are the things that you can’t do with generics (and by “can’t,” we mean without resorting to contortions like passing in a class object):

  • Calling a constructor: Writing new T() is not allowed because at runtime it would be new Object().
  • Creating an array of that generic type: This one is the most annoying, but it makes sense because you’d be creating an array of Object values.
  • Calling instanceof: This is not allowed because at runtime List<Integer> and List<String> look the same to Java thanks to type erasure.
  • Using a primitive type as a generic type parameter: This isn’t a big deal because you can use the wrapper class instead. If you want a type of int, just use Integer.
  • Creating a static variable as a generic type parameter: This is not allowed because the type is linked to the instance of the class.
80
Q

GENERIC METHODS

A

test

81
Q

OPTIONAL SYNTAX FOR INVOKING A GENERIC METHOD

A

test

82
Q

BOUNDING GENERIC TYPES

A

test

83
Q

Unbounded Wildcards

A

test

84
Q

Upper‐Bounded Wildcards

A

test

85
Q

Lower‐Bounded Wildcards

A

test

86
Q

UNDERSTAND GENERIC SUPERTYPES

A

test

87
Q

PUTTING IT ALL TOGETHER

A

test

88
Q

Combining Generic Declarations

A

test

89
Q

Passing Generic Arguments

A

test