用于添加到哈希映射中的列表的快捷方式

2022-08-31 12:44:43

我经常需要获取对象列表,并根据对象中包含的值将它们分组到Map中。例如。获取用户列表并按国家/地区分组。

我的代码通常如下所示:

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);
    }
}

然而,我不禁认为这是尴尬的,一些大师有更好的方法。到目前为止,我能看到的最接近的是Google Collections的MultiMap

是否有任何标准方法?

谢谢!


答案 1

从Java 8开始,你可以使用Map#computeIfAbsent()。

Map<String, List<User>> usersByCountry = new HashMap<>();

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

或者,利用 Stream API 的 Collectors#groupingBy() 直接从 转到:ListMap

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

在Java 7或更低版本中,您可以获得的最好方法是:

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有一个LazyMap,但它没有参数化。番石榴没有一种或,但你可以使用Multimap来做到这一点,如下面的多基因elubricants的答案所示。LazyMapLazyList


答案 2

Guava的Multimap确实是最适合此的数据结构,实际上,有Multimaps.index(Iterable<V>,Function<?super V,K>)实用程序方法,它可以完全按照您的要求:采用可迭代<V>List<V>是),然后应用Function<?super V,K>来获取.Multimap<K,V>

下面是文档中的一个示例:

例如

  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);

指纹

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

在你的情况下,你会写一个.Function<User,String> userCountryFunction = ...