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!
Advertisement
Answer
Since 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.