繁体   English   中英

在 HashMap 中添加到列表的快捷方式

[英]Shortcut for adding to List in a HashMap

我经常需要获取一个对象列表,然后根据 object 中包含的值将它们分组到 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

有没有标准的方法?

谢谢!

从 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()直接从List转到Map

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 ,但它没有参数化。 Guava没有某种LazyMapLazyList ,但您可以为此使用Multimap ,如下面的 polygenelubricants 答案所示

Guava 的Multimap确实是最合适的数据结构,事实上,有Multimaps.index(Iterable<V>, Function<? super V,K>)实用方法可以满足您的要求:采用Iterable<V>List<V>是),并应用Function<? super V, K> 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 = ...

我们似乎经常这样做,所以我创建了一个模板类

public abstract class ListGroupBy<K, T> {
public Map<K, List<T>> map(List<T> list) {
    Map<K, List<T> > map = new HashMap<K, List<T> >();
    for (T t : list) {
        K key = groupBy(t);
        List<T> innerList = map.containsKey(key) ? map.get(key) : new ArrayList<T>();
        innerList.add(t);
        map.put(key, innerList);
    }
    return map;
}

protected abstract K groupBy(T t);
}

您只需为 groupBy 提供 impl

在你的情况下

String groupBy(User u){return user.getCountry();}

当我必须处理一个集合值映射时,我几乎总是在类中编写一个小的 putIntoListMap() 静态实用程序方法。 如果我发现自己在多个类中都需要它,我会将该方法放入实用程序类中。 像这样的静态方法调用有点难看,但它们比每次都输入代码要干净得多。 除非多地图在您的应用程序中扮演非常重要的角色,否则恕我直言,引入另一个依赖项可能不值得。

通过使用lambdaj ,您只需一行代码即可获得该结果,如下所示:

Group<User> usersByCountry = group(listOfUsers, by(on(User.class).getCountry()));

Lambdaj 还提供了许多其他功能来使用非常易读的领域特定语言来操作集合。

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

GC 库中的LinkedHashMultimap似乎满足了您的确切需求。 如果您可以忍受依赖项,那么您的所有代码都将变为:

SetMultimap<String,User> countryToUserMap = LinkedHashMultimap.create();
// .. other stuff, then whenever you need it:
countryToUserMap.put(user.getCountry(), user);

维护插入顺序(看起来就像您对列表所做的一切)并且排除了重复项; 您当然可以根据需要切换到基于散列的普通集或树集(或列表,尽管这似乎不是您所需要的)。 如果您要求一个没有用户的国家/地区,每个人都有小马等,则会返回空集合 - 我的意思是,请查看 API。 它会为你做很多事情,所以依赖可能是值得的。

添加元素的一种简洁易读的方法如下:

String country = user.getCountry();
Set<User> users
if (users.containsKey(country))
{
    users = usersByCountry.get(user.getCountry());
}
else
{
    users = new HashSet<User>();
    usersByCountry.put(country, users);
}
users.add(user);

请注意,调用containsKeyget并不比仅调用get并测试null的结果慢。

ArrayList numbersList = new ArrayList<>(Arrays.asList(1, 1, 2, 3, 3, 3, 4, 5, 6, 6, 6, 7, 8));

Map<Integer, Long> elementCountMap = numbersList.stream().collect(Collectors.toMap(Function.identity(), v -> 1L, Long::sum));

System.out.println(elementCountMap);

o/p:{1=2, 2=1, 3=3, 4=1, 5=1, 6=3, 7=1, 8=1}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM