![Stop writing loops! Top 10 best practices for working with collections in Java 8 - 1]()
As you know, our habits are second nature. And once you get used to writing
for (int i = 0; i <......)
, no part of you wants to have to relearn this construct (especially since it is quite simple and understandable). However, loops are often repeatedly used to perform the same basic operations, and repetition is something we would very much like to get rid of. With Java 8, Oracle has decided to help us do this. Below are the 10 best collection methods that will save you a ton of time and code.
1. Iterable.forEach(Consumer <? Super T> action)
The name speaks for itself. It iterates over the collection passed as an argument, and executes the action lambda expression for each of its elements.
List <Integer> numbers = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7));
numbers.forEach(s -> System.out.print(s + " "));
1 2 3 4 5 6 7
2. Collection.removeIf(Predicate<? super E> filter)
Again, nothing difficult here. The method iterates over the collection and removes any elements that match
filter
.
List <Integer> numbers = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7));
numbers.removeIf(s -> s > 5);
numbers.forEach(s -> System.out.print(s + " "));
In a single line, we are removing from the list all numbers greater than 5.
3. Map.forEach(BiConsumer <? super K, ? super V> action)
The
forEach
method works not only for classes that implement the
Collection
interface, but also for
Map
.
Map <String, String> books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
books.forEach((a,b) -> System.out.println("Book title: " + a + ". Author: "+ b));
Book title: The Brothers Karamazov. Author: Fyodor Dostoevsky
Book title: Thinking in Java. Author: Bruce Eckel
Book title: Crime and Punishment. Author: Fyodor Dostoevsky
Book title: War and Peace. Author: Leo Tolstoy
Book title: Lord of the Rings. Author: John Tolkien
4. Map.compute (K key, BiFunction<? Super K,? Super V,? Extends V> remappingFunction)
Looks a little more intimidating, but in fact simple, like all the previous ones. This method sets
key
's value equal to result of executing
mappingFunction
. For example:
Map <String, String> books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
books.forEach((a,b) -> System.out.println("Book title: " + a + ". Author: "+ b));
books.compute("Thinking in Java", (a,b) -> b + ", cool dude");
System.out.println("_______________________");
books.forEach((a,b) -> System.out.println("Book title: " + a + ". Author: "+ b));
Book title: The Brothers Karamazov. Author: Fyodor Dostoevsky
Book title: Thinking in Java. Author: Bruce Eckel
Book title: Crime and Punishment. Author: Fyodor Dostoevsky
Book title: War and Peace. Author: Leo Tolstoy
Book title: Lord of the Rings. Author: John Tolkien
_______________________
Book title: The Brothers Karamazov. Author: Fyodor Dostoevsky
Book title: Thinking in Java. Author: Bruce Eckel, cool dude
Book title: Crime and Punishment. Author: Fyodor Dostoevsky
Book title: War and Peace. Author: Leo Tolstoy
Book title: Lord of the Rings. Author: John Tolkien
The author of "Thinking in Java" is definitely cool! :)
5. Map.computeIfAbsent(K key, Function <? super K, ? extends V> mappingFunction)
This method will add a new element to the
Map
, but only if it does not already have an element with that key. The assigned value will the result of executing the
mappingFunction
.
If an element with the key already exists, it will not be overwritten. It will simply remain as is.
Let's return to our books and try a new method:
Map <String, String> books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
books.computeIfAbsent("Harry Potter and the Prisoner of Azkaban", b -> getHarryPotterAuthor());
books.forEach((a,b) -> System.out.println("Book title: " + a + ". Author: "+ b));
Here's our
mappingFunction
:
public static String getHarryPotterAuthor() {
return "Joanne Rowling";
}
And here's the new book:
Book title: The Brothers Karamazov. Author: Fyodor Dostoevsky
Book title: Thinking in Java. Author: Bruce Eckel
Book title: Crime and Punishment. Author: Fyodor Dostoevsky
Book title: War and Peace. Author: Leo Tolstoy
Book title: Harry Potter and the Prisoner of Azkaban. Author: Joanne Rowling
Book title: Lord of the Rings. Author: John Tolkien
6. Map.computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
Here we have the same principle as
Map.compute()
, but the calculations are performed only if an item with
key
already exists.
Map <String, String> books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
books.computeIfPresent("Eugene Onegin", (a,b) -> b = "Alexander Pushkin");
System.out.println("_________________");
books.forEach((a,b) -> System.out.println("Book title: " + a + ". Author: "+ b));
books.computeIfPresent("The Brothers Karamazov", (a,b) -> b = "Alexander Pushkin");
System.out.println("_________________");
books.forEach((a,b) -> System.out.println("Book title: " + a + ". Author: "+ b));
The first call to the function made no changes, because there is no book entitled "Eugene Onegin" in our
Map
.
But in the second call, the program changed the author of the book "The Brothers Karamazov" to Alexander Pushkin.
Output:
_________________
Book title: The Brothers Karamazov. Author: Fyodor Dostoevsky
Book title: Thinking in Java. Author: Bruce Eckel
Book title: Crime and Punishment. Author: Fyodor Dostoevsky
Book title: War and Peace. Author: Leo Tolstoy
Book title: Lord of the Rings. Author: John Tolkien
_________________
Book title: The Brothers Karamazov. Author: Alexander Pushkin
Book title: Thinking in Java. Author: Bruce Eckel
Book title: Crime and Punishment. Author: Fyodor Dostoevsky
Book title: War and Peace. Author: Leo Tolstoy
Book title: Lord of the Rings. Author: John Tolkien
7. Map.getOrDefault(Object key, V defaultValue)
This method returns the value corresponding to
key
. If the key does not exist, then it returns the default value.
Map <String, String> books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
String igor = books.getOrDefault("The Tale of Igor's Campaign", "Unknown author");
System.out.println(igor);
This is very convenient:
Unknown author
8. Map.merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
I didn't even bother trying to calculate how many lines of code this method will save you.
- If
key
does not exist in your Map
, or if the value
for this key is null
, then the method adds the passed key-value
pair to the Map
.
- If
key
does exist and its value != null
, then the method changes its value to the result of executing remappingFunction
.
- If
remappingFunction
returns null
, then key
is removed from the collection.
Map <String, String> books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
books.merge("Thinking in Java", "Bruce Eckel", (a, b) -> b + " and some coauthor");
books.forEach((a, b) -> System.out.println("Title: " + a + ". Author: "+ b));
Output:
Title: The Brothers Karamazov. Author: Fyodor Dostoevsky
Title: Thinking in Java. Author: Bruce Eckel and some coauthor
Title: Crime and Punishment. Author: Fyodor Dostoevsky
Title: War and Peace. Author: Leo Tolstoy
Title: Lord of the Rings. Author: John Tolkien
*sorry, Bruce*
9. Map.putIfAbsent(K key, V value)
Previously, to add a pair to a
Map
, if it was not already there, you had to do the following:
Map <String, String> map = new HashMap<>();
if (map.get("Lord of the Rings") == null)
map.put("Lord of the Rings", "John Tolkien");
Now everything has become much easier:
Map<String, String> map = new HashMap<>();
map.putIfAbsent("Lord of the Rings", "John Tolkien");
10. Map.replace and Map.replaceAll()
Last but not least.
Map.replace(K key, V newValue)
replaces key
's value with newValue
, if such a key exists. If not, nothing happens.
Map.replace(K key, V oldValue, V newValue)
does the same thing, but only if the current value for key
is equal to oldValue
.
Map.replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
replaces each value
with the result of function.
For example:
Map <String, String> books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
books.replace("The Brothers Karamazov", "Bruce Eckel", "John Tolkien");
books.forEach((a, b) -> System.out.println("Title: " + a + ". Author: "+ b));
Title: The Brothers Karamazov. Author: Fyodor Dostoevsky
Title: Thinking in Java. Author: Bruce Eckel
Title: Crime and Punishment. Author: Fyodor Dostoevsky
Title: War and Peace. Author: Leo Tolstoy
Title: Lord of the Rings. Author: John Tolkien
It didn't work! The current value for the key "The Brothers Karamazov" is "Fyodor Dostoevsky", not "Bruce Eckel", so nothing has changed.
Map books = new HashMap<>();
books.put("War and Peace", "Leo Tolstoy");
books.put("Crime and Punishment", "Fyodor Dostoevsky");
books.put("Thinking in Java", "Bruce Eckel");
books.put("The Brothers Karamazov", "Fyodor Dostoevsky");
books.put("The Lord of the Rings", "John Tolkien");
books.replaceAll((a,b) -> getCoolAuthor());
books.forEach((a, b) -> System.out.println("Title: " + a + ". Author: "+ b));
public static String getCoolAuthor() {
return "Cool author";
}
Title: The Brothers Karamazov. Author: Cool author
Title: Thinking in Java. Author: Cool author
Title: Crime and Punishment. Author: Cool author
Title: War and Peace. Author: Cool author
Title: Lord of the Rings. Author: Cool author
We easily changed the values for the entire
Map
without any complicated constructs!
P.S. Getting used to the new is always difficult, but these changes are really good. In any event, some parts of my code are definitely less like spaghetti than before :) Good luck in learning!
GO TO FULL VERSION