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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3019376/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:38:30  来源:igfitidea点击:

Shortcut for adding to List in a HashMap

javacollectionshashmap

提问by Damo

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.

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

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.

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

Are there any standard approaches?

有没有标准的方法?

Thanks!

谢谢!

采纳答案by BalusC

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

在 Java 8 中,您可以使用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 Listto Mapdirectly:

或者,使用 Stream API直接Collectors#groupingBy()ListMap

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

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

在 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 Collectionshas a LazyMap, but it's not parameterized. Guavadoesn't have sort of a LazyMapor LazyList, but you can use Multimapfor this as shown in answer of polygenelubricants below.

Commons Collections有一个LazyMap,但它没有参数化。Guava没有 aLazyMapLazyList,但您可以使用Multimap它,如下面的 polygenelubricants 的回答所示

回答by Luke Maurer

When I have to deal with a collection-valued map, I just about always wind up writing a little putIntoListMap() static utility method in the class. If I find myself needing it in multiple classes, I throw that method into a utility class. Static method calls like that are a bit ugly, but they're much cleaner than typing the code out every time. Unless multi-maps play a pretty central role in your app, IMHO it's probably not worth it to pull in another dependency.

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

回答by Carl

It looks like your exact needs are met by LinkedHashMultimapin the GC library. If you can live with the dependencies, all your code becomes:

看起来您的确切需求由GC 库中的LinkedHashMultimap满足。如果你可以忍受依赖,你的所有代码都会变成:

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

insertion order is maintained (about all it looks like you were doing with your list) and duplicates are precluded; you can of course switch to a plain hash-based set or a tree set as needs dictate (or a list, though that doesn't seem to be what you need). Empty collections are returned if you ask for a country with no users, everyone gets ponies, etc - what I mean is, check out the API. It'll do a lot for you, so the dependency might be worth it.

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

回答by starblue

A clean and readable way to add an element is the following:

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

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

Note that calling containsKeyand getis not slower than just calling getand testing the result for null.

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

回答by polygenelubricants

Guava's Multimapreally 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>.

GuavaMultimap确实是最合适的数据结构,事实上,有一种Multimaps.index(Iterable<V>, Function<? super V,K>)实用方法可以完全满足您的需求:取一个Iterable<V>(aList<V>是),然后应用Function<? super V, K>来获取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]}

例如,

  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]}

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

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

回答by Mario Fusco

By using lambdajyou can obtain that result with just one line of code as it follows:

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

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

Lambdaj also offers lots of other features to manipulate collections with a very readable domain specific language.

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

回答by Jukey

We seem to do this a lot of times so I created a template class

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

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

You just provide impl for groupBy

您只需为 groupBy 提供 impl

in your case

在你的情况下

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

回答by user1529313

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