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
Shortcut for adding to List in a HashMap
提问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 Collections的MultiMap。
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 List
to Map
directly:
或者,使用 Stream API直接Collectors#groupingBy()
从List
到Map
:
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 LazyMap
or LazyList
, but you can use Multimap
for this as shown in answer of polygenelubricants below.
Commons Collections有一个LazyMap
,但它没有参数化。Guava没有 aLazyMap
或LazyList
,但您可以使用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 containsKey
and get
is not slower than just calling get
and testing the result for null
.
请注意,调用containsKey
和get
并不比仅调用get
和测试 的结果慢null
。
回答by polygenelubricants
Guava's Multimap
really 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);
}