Date:

Share:

Using Streams with Map in Java 8

Related Articles

Java 8 stream is a widely used feature for writing code in a functional programming way. In this tutorial, we will discuss how to use streams to create maps, iteration and sort.

Let’s create a User class and user list, which we will use in all the examples of this tutorial: –

 class User 
    Long id;
    String name;
    Integer age;

    // constructor, getters, setters, toString


List<User> users = List.of(new User(1L, "Andrew", 23),
            new User(2L, "Billy", 42),
            new User(3L, "David", 29),
            new User(4L, "Charlie", 30),
            new User(5L, "Andrew", 18),
            new User(6L, "Charlie", 19));

Notice that User ID is unique and The username is not unique. You can see multiple users with the name, i.e. Andrew and Charlie. We have intentionally saved them so that they can be used in our examples.

Create a map

A map is created when you collect a stream of elements using one of them Collectors.toMap() or Collectors.groupingBy().

Using Collectors.toMap ()

Example 1: Map of streams with a unique key

Let’s broadcast the list and collect it on a map using Collectors.toMap(keyMapper, valueMapper) When a key is a unique user ID and the value is the username that can be duplicated: –

Map<Long, String> map = users.stream()
            .collect(Collectors.toMap(User::getId, User::getName));

// 1=Andrew, 2=Billy, 3=David, 4=Charlie, 5=Andrew, 6=Charlie

Another example of creating a map using a unique user ID as key And user Object value: –

Map<Long, User> map = users.stream()
            .collect(Collectors.toMap(User::getId, Function.identity()));

//1=Userid=1, name='Andrew', age=23,
// 2=Userid=2, name='Billy', age=42,
// 3=Userid=3, name='David', age=29, 
// 4=Userid=4, name='Charlie', age=30, 
// 5=Userid=5, name='Andrew', age=18, 
// 6=Userid=6, name='Charlie', age=19

Example 2: Map of streams with a double key

In previous examples, we used a user ID as a key that works perfectly because a map key needs to be unique.

Dual Duplicate Results Error!

Let’s see what happens when we use a username as a non-unique key and the age of the user as a value: –

Map<String, Integer> map = users.stream()
      .collect(Collectors.toMap(User::getName, User::getAge));

It throws IllegalStateException Which is to be expected since the key to a map should be unique

java.lang.IllegalStateException: Duplicate key Andrew (attempted merging values 23 and 18)
mergeFunction to rescue!

Java 8 Streams Provider Collectors.toMap(keyMapper, valueMapper, mergeFunction) An overloaded method where you can specify which value to consider when a dual key problem occurs.

Let’s we collect a map with a username as a key, a merge function specifies the old value of that key:

Map<String, Integer> idValueMap = users.stream()
    .collect(Collectors.toMap(User::getName, User::getAge, (oldValue, newValue) -> oldValue));

// Billy=42, Andrew=23, Charlie=30, David=29

We do not see any error this time and a map is created with unique usernames. Duplicate usernames merge with the earliest age value on the list.

Example 3: ConcurrentHashMap, LinkedHashMap and TreeMap streams

Java 8 Streams Provider Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapFactory) Too busy method where you can specify the type using mapFactory to return ConcurrentHashMap, LinkedHashMap or TreeMap.

 Map<String, Integer> concurrentHashMap = users.stream()
            .collect(Collectors.toMap(User::getName, User::getAge, (o1, o2) -> o1, ConcurrentHashMap::new));

 Map<String, Integer> linkedHashMap = users.stream()
            .collect(Collectors.toMap(User::getName, User::getAge, (o1, o2) -> o1, LinkedHashMap::new));

 Map<String, Integer> treeMap = users.stream()
            .collect(Collectors.toMap(User::getName, User::getAge, (o1, o2) -> o1, TreeMap::new));          

Using Collectors.groupingBy ()

Map is returned when you group a stream of objects using Collectors.groupingBy(keyMapper, valueMapper). You can specify the key and value mapping function. Specifying the value mapping function is optional and returns a list by default.

Example 1: Group the stream by key

Let’s group the user object stream by name using Collectors.groupingBy(keyMapper) Which returns a map with a key as a username and value as a list of user object matching this key: –

Map<String, List<User>> groupByName = users.stream()
            .collect(Collectors.groupingBy(User::getName));

// Billy=[Userid=2, name='Billy', age=42], 
//  Andrew=[Userid=1, name='Andrew', age=23, Userid=5, name='Andrew', age=18],
//  Charlie=[Userid=4, name='Charlie', age=30, Userid=6, name='Charlie', age=19], 
//  David=[Userid=3, name='David', age=29]

Example 2: Group the current by key and value

This time we will specify the key mapping and value function in Collectors.groupingBy(keyMapper, valueMapper). for example:-

Count the users with the same name, where the key is the username and the value is a count: –

 Map<String, Long> countByName = users.stream()
            .collect(Collectors.groupingBy(User::getName, Collectors.counting()));

// Billy=1, Andrew=2, Charlie=2, David=1

The age amount of users with the same name, where the key is the username and the value is the age amount: –

Map<String, Integer> sumAgeByName = users.stream()
        .collect(Collectors.groupingBy(User::getName, Collectors.summingInt(User::getAge)));

// Billy=42, Andrew=41, Charlie=49, David=29

Go back through a map

There are three ways to repeat a map:

Using keySet ()

The method keySet() Applied to Map<K,V> Which returns Set<K> And can be swiped to repeat the keys: –

users.stream()
        .collect(Collectors.toMap(User::getId, User::getName))
        .keySet()
        .stream()
        .forEach(System.out::print);
      
// Prints "1 2 3 4 5"

Use of values ​​()

The method values() Applied to Map<K,V> Which returns Collection<V> And you can stream them to repeat values: –

users.stream()
        .collect(Collectors.toMap(User::getId, User::getName))
        .values()
        .stream()
        .forEach(System.out::print);

// Prints "Andrew Billy David Charlie Andrew Charlie"

Using entrySet ()

The method entrySet() Applied to Map<K,V> Which returns Set<Map.Entry<K, V>> And can be streamed to repeat values ​​(keys and values): –

users.stream()
        .collect(Collectors.toMap(User::getId, User::getName))
        .entrySet()
        .stream()
        .forEach(System.out::print);

// Prints "1=Andrew 2=Billy 3=David 4=Charlie 5=Andrew 6=Charlie"

Sort the map

By key

We can sort the map by key using streams with a built-in comparison Map.Entry.comparingByKey()

Sort the map by Alphabetical key Order and print it: –

users.stream()
        .collect(Collectors.toMap(User::getName, User::getAge, (o1,o2) -> o1))
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByKey())
        .forEach(System.out::println);

// Andrew=23
// Billy=42
// Charlie=30
// David=29

Sort the map by Invert alphabet key Order and collect for LinkedHashMap: –

Map<String, Integer> sortByKeyReverse = users.stream()
        .collect(Collectors.toMap(User::getName, User::getAge, (o1,o2) -> o1))
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (o1,o2) -> o1, LinkedHashMap::new));

// David=29, Charlie=30, Billy=42, Andrew=23

By value

We can sort the map by value using streams with a built-in comparator Map.Entry.comparingByValue()

Sort the map by Rise value Order and print it: –

users.stream()
        .collect(Collectors.toMap(User::getName, User::getAge, (o1,o2) -> o1))
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByValue()).forEach(System.out::println);

// Andrew=23
// David=29
// Charlie=30
// Billy=42

Sort the map by Declining value Order and collect for LinkedHashMap: –

Map<String, Integer> sortByValueReverse = users.stream()
        .collect(Collectors.toMap(User::getName, User::getAge, (o1,o2) -> o1))
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (o1,o2) -> o1, LinkedHashMap::new));

// Billy=42, Charlie=30, David=29, Andrew=23

Both by key and by value

We can sort the map by using the key and about one after the other using thenComparing()

Sort the map by Alphabetical entry Order and Then sort by Key in decline Order and collect for LinkedHashMap: –

Comparator<Map.Entry<Long, String>> valueComparator = Map.Entry.comparingByValue();
Comparator<Map.Entry<Long, String>> keyComparator = Map.Entry.comparingByKey(Comparator.reverseOrder());

Map<Long, String> sortByValueThenKey = users.stream()
    .collect(Collectors.toMap(User::getId, User::getName))
    .entrySet()
    .stream()
    .sorted(valueComparator.thenComparing(keyComparator))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (o1, o2) -> o1, LinkedHashMap::new));

// 5=Andrew, 1=Andrew, 2=Billy, 6=Charlie, 4=Charlie, 3=David

We got a sorted map with usernames sorted alphabetically first and then the keys sorted in descending order of the user ID.

Source

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Popular Articles