Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shortcut for adding to List in a HashMap

I often have a need to take a list of objects and group them into a Map based on a value contained in the object. Eg. take a list of Users and group by Country.

My code for this usually looks like:

Map<String, List<User>> usersByCountry = new HashMap<String, List<User>>(); for(User user : listOfUsers) {     if(usersByCountry.containsKey(user.getCountry())) {         //Add to existing list         usersByCountry.get(user.getCountry()).add(user);      } else {         //Create new list         List<User> users = new ArrayList<User>(1);         users.add(user);         usersByCountry.put(user.getCountry(), users);     } } 

However I can't help thinking that this is awkward and some guru has a better approach. The closest I can see so far is the MultiMap from Google Collections.

Are there any standard approaches?

Thanks!

like image 568
Damo Avatar asked Jun 10 '10 23:06

Damo


People also ask

How do you add to a list in Java?

There are two methods to add elements to the list. add(E e): appends the element at the end of the list. Since List supports Generics, the type of elements that can be added is determined when the list is created. add(int index, E element): inserts the element at the given index.


2 Answers

In Java 8 you can make use of Map#computeIfAbsent().

Map<String, List<User>> usersByCountry = new HashMap<>();  for (User user : listOfUsers) {     usersByCountry.computeIfAbsent(user.getCountry(), k -> new ArrayList<>()).add(user); } 

Or, make use of Stream API's Collectors#groupingBy() to go from List to Map directly:

Map<String, List<User>> usersByCountry = listOfUsers.stream().collect(Collectors.groupingBy(User::getCountry)); 

In Java 7 or below, best what you can get is below:

Map<String, List<User>> usersByCountry = new HashMap<>();  for (User user : listOfUsers) {     List<User> users = usersByCountry.get(user.getCountry());     if (users == null) {         users = new ArrayList<>();         usersByCountry.put(user.getCountry(), users);     }     users.add(user); } 

Commons Collections has a LazyMap, but it's not parameterized. Guava doesn't have sort of a LazyMap or LazyList, but you can use Multimap for this as shown in answer of polygenelubricants below.

like image 198
BalusC Avatar answered Sep 21 '22 19:09

BalusC


Guava's Multimap really is the most appropriate data structure for this, and in fact, there is Multimaps.index(Iterable<V>, Function<? super V,K>) utility method that does exactly what you want: take an Iterable<V> (which a List<V> is), and apply the Function<? super V, K> to get the keys for the Multimap<K,V>.

Here's an example from the documentation:

For example,

  List<String> badGuys       = Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");   Function<String, Integer> stringLengthFunction = ...;   Multimap<Integer, String> index       = Multimaps.index(badGuys, stringLengthFunction);   System.out.println(index); 

prints

 {4=[Inky], 5=[Pinky, Pinky, Clyde], 6=[Blinky]} 

In your case you'd write a Function<User,String> userCountryFunction = ....

like image 38
polygenelubricants Avatar answered Sep 21 '22 19:09

polygenelubricants