Chapter 14 Generics and Collections Flashcards
Functional interfaces
-
Supplier<T>
T get()
-
Consumer<T>
void accept(T t)
-
BiConsumer<T, U>
void accept(T t, U u)
-
Predicate<T>
boolean test(T t)
-
BiPredicate<T, U>
boolean test(T t, U u)
-
Function<T, R>
R apply(T t)
-
BiFunction<T, U, R>
R apply(T t, U u)
-
UnaryOperator<T>
static <T> UnaryOperator<T> identity()
Using Method References
@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.
four types of method references
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
> [!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.
CALLING STATIC METHODS
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.
CALLING INSTANCE METHODS ON A PARTICULAR OBJECT
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();
CALLING INSTANCE METHODS ON A PARAMETER
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.
CALLING CONSTRUCTORS
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.
REVIEWING METHOD REFERENCES
Method references
NUMBER OF PARAMETERS IN A METHOD REFERENCE
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.
Using Wrapper Classes
- 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
- Boolean.valueOf(true)
- Byte.valueOf((byte) 1)
- Short.valueOf((short) 1)
- Integer.valueOf(1)
- Long.valueOf(1)
- Float.valueOf((float) 1.0)
- Double.valueOf(1.0)
- Character.valueOf(‘c’)
Can you spot the autoboxing and unboxing in this example?
12: Integer pounds = 120; 13: Character letter = "robot".charAt(0); 14: char r = letter;
- 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.
This innocuous-looking code throws an exception:
15: var heights = new ArrayList<Integer>(); 16: heights.add(null); 17: int h = heights.get(0); // NullPointerException
- 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.
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);
> [!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].
Using the Diamond Operator
- 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 }
Do you think these two statements compile and are equivalent?
var list = new ArrayList<Integer>(); var list = new ArrayList<>();
- The first one creates an
ArrayList<Integer>
- The second one creates an
ArrayList<Object>
Using Lists Sets Maps and Queues
- A
collection
is a group of objects contained in a single object. - The
Java Collections Framework
is a set of classes injava.util
for storing collections. - There are four main interfaces in the Java Collections Framework.
-
List: A list is an
ordered
collection of elements that allowsduplicate
entries. Elements in a list can be accessed by an intindex
. -
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 afirst-in, first-out
order, but other orderings are possible. -
Map: A map is a collection that maps
keys
tovalues
, withno duplicate keys
allowed. The elements in a map arekey/value pairs
.
-
List: A list is an
Map doesn’t implement the Collection interface.
COMMON COLLECTIONS METHODS
- inserts a new element into the Collection and returns whether it was successful.
boolean add(E element)
- removes a single matching value in the Collection and returns whether it was successful.
boolean remove(Object object)
- The isEmpty() and size() methods look at how many elements are in the Collection.
boolean isEmpty()
int size()
- The clear() method provides an easy way to discard all elements of the Collection.
void clear()
- The contains() method checks whether a certain value is in the Collection.
boolean contains(Object object)
- The removeIf() method removes all elements that match a condition.
boolean removeIf(Predicate<? super E> filter)
- Looping through a Collection.
void forEach(Consumer<? super T> action)
add()
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
remove()
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
.
DELETING WHILE LOOPING
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);
isEmpty() and size()
The isEmpty() and size() methods look at how many elements are in the Collection.
The method signatures are as follows:boolean isEmpty()
int size()
clear()
The clear() method provides an easy way to discard all elements of the Collection. The method signature is as follows:void clear()
contains()
The contains() method checks whether a certain value is in the Collection.
The method signature is as follows:boolean contains(Object object)