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

Java 在HashMap中添加到列表的快捷方式,java,collections,hashmap,Java,Collections,Hashmap,我经常需要获取对象列表,并根据对象中包含的值将它们分组到地图中。按国家列出用户和组的列表 我的代码通常如下所示: Map<String, List<User>> usersByCountry = new HashMap<String, List<User>>(); for(User user : listOfUsers) { if(usersByCountry.containsKey(user.getCountry())) {

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

我的代码通常如下所示:

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);
    }
}
然而,我忍不住认为这很尴尬,有些大师有更好的方法。到目前为止,我能看到的最近的一个是

有什么标准方法吗


谢谢

在Java8中,您可以使用

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

具有,但未参数化。没有LazyMap或LazyList,但您可以使用它,如中所示。

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

看起来您的确切需求在GC库中得到了满足。如果您可以接受依赖项,那么您的所有代码都将变成:

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);
请注意,调用containsKey和get并不比只调用get并测试结果是否为null慢。

Guava's确实是最适合这种情况的数据结构,事实上,有一种实用方法可以完全满足您的需要:取a which a is,并应用该方法来获取多重映射的键

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

比如说,

  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);
在本例中,您可以使用编写一个函数userCountryFunction=..

,只需一行代码即可获得该结果,如下所示:

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

Lambdaj还提供了许多其他功能,可以用一种可读性很强的领域特定语言来操作集合。

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

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

那真的应该是地图吗?答案会对您选择构建或使用的内容产生影响。请注意,Google Collections为各种类型的列表和集合的嵌套集合提供了改进。只需删除Java for.Net和Linq即可。@Hamish:是的,因为我们对依赖关系的担心完全无关@哈米什:如果我那样做的话,我再也不能面对我的.Net编码兄弟了。真丢脸+1谢谢,知道这一点很好,但BalusC的optinmization正是我所追求的。调用本身确实不慢,但查找现在发生了两次而不是一次。+1我很沮丧,涉及编写比这多得多的代码的答案排名更高,只是因为它们是最快进入的答案。:@凯文:我希望你最终能来看看我,顺便说一句,我计划最终在各种番石榴类的stackoverflow上写一些Q/A文章来展示它的功能。我每天只来一两次,这样我就保证我永远不会有机会得到我的答案。我认为你的想法很好。我想你的意思是发布一个问题并自己回答。你会听到一些人告诉你这件事有点不道德,但它得到了更广泛的SO社区的明确认可,因为他们的目标是让SO拥有伟大的内容。例如+1。我对这些文章很好奇。Guava库应该引起更多的注意,您可以进一步缩短它:usersByCountry.putuser.getCountry,users=newarraylist;虽然我相信有些人会对此表示不满。我知道这并不重要,但也许新手需要知道映射函数将以键作为参数,因此最好使用“k”而不是“v”usersByCountry.computeifassentuser.getCountry,k->new ArrayList.adduser;
 {4=[Inky], 5=[Pinky, Pinky, Clyde], 6=[Blinky]}
Group<User> usersByCountry = group(listOfUsers, by(on(User.class).getCountry()));
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);
}
String groupBy(User u){return user.getCountry();}
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);
}