Java:如何将列表转换为地图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4138364/
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
Java: How to convert List to Map
提问by Rachel
Recently I have conversation with a colleague about what would be the optimal way to convert List
to Map
in Java and if there any specific benefits of doing so.
最近,我与一位同事讨论了在 Java 中转换List
为的最佳方式,Map
以及这样做是否有任何具体好处。
I want to know optimal conversion approach and would really appreciate if any one can guide me.
我想知道最佳转换方法,如果有人可以指导我,我将不胜感激。
Is this good approach:
这是好方法吗:
List<Object[]> results;
Map<Integer, String> resultsMap = new HashMap<Integer, String>();
for (Object[] o : results) {
resultsMap.put((Integer) o[0], (String) o[1]);
}
采纳答案by Jim Garrison
List<Item> list;
Map<Key,Item> map = new HashMap<Key,Item>();
for (Item i : list) map.put(i.getKey(),i);
Assuming of course that each Item has a getKey()
method that returns a key of the proper type.
当然,假设每个 Item 都有一个getKey()
返回正确类型键的方法。
回答by Daniel
Many solutions come to mind, depending on what you want to achive:
根据您想要实现的目标,您会想到许多解决方案:
Every List item is key and value
每个列表项都是键和值
for( Object o : list ) {
map.put(o,o);
}
List elements have something to look them up, maybe a name:
列表元素有一些东西可以查找它们,也许是一个名称:
for( MyObject o : list ) {
map.put(o.name,o);
}
List elements have something to look them up, and there is no guarantee that they are unique: Use Googles MultiMaps
列表元素有一些东西可以查找,并且不能保证它们是唯一的:使用 Googles MultiMaps
for( MyObject o : list ) {
multimap.put(o.name,o);
}
Giving all the elements the position as a key:
将所有元素的位置作为键:
for( int i=0; i<list.size; i++ ) {
map.put(i,list.get(i));
}
...
...
It really depends on what you want to achive.
这实际上取决于您想要实现的目标。
As you can see from the examples, a Map is a mapping from a key to a value, while a list is just a series of elements having a position each. So they are simply not automatically convertible.
从示例中可以看出,Map 是从键到值的映射,而列表只是一系列元素,每个元素都有一个位置。所以它们根本不能自动转换。
回答by Steve Kuo
A List
and Map
are conceptually different. A List
is an ordered collection of items. The items can contain duplicates, and an item might not have any concept of a unique identifier (key). A Map
has values mapped to keys. Each key can only point to one value.
AList
和Map
在概念上是不同的。AList
是项目的有序集合。项目可以包含重复项,并且项目可能没有任何唯一标识符(键)的概念。AMap
具有映射到键的值。每个键只能指向一个值。
Therefore, depending on your List
's items, it may or may not be possible to convert it to a Map
. Does your List
's items have no duplicates? Does each item have a unique key? If so then it's possible to put them in a Map
.
因此,根据您List
的项目,可能会也可能不会将其转换为Map
. 你List
的物品没有重复吗?每个项目都有唯一的键吗?如果是这样,则可以将它们放入Map
.
回答by Kango_V
Here's a little method I wrote for exactly this purpose. It uses Validate from Apache Commons.
这是我为此目的编写的一个小方法。它使用来自 Apache Commons 的验证。
Feel free to use it.
随意使用它。
/**
* Converts a <code>List</code> to a map. One of the methods of the list is called to retrive
* the value of the key to be used and the object itself from the list entry is used as the
* objct. An empty <code>Map</code> is returned upon null input.
* Reflection is used to retrieve the key from the object instance and method name passed in.
*
* @param <K> The type of the key to be used in the map
* @param <V> The type of value to be used in the map and the type of the elements in the
* collection
* @param coll The collection to be converted.
* @param keyType The class of key
* @param valueType The class of the value
* @param keyMethodName The method name to call on each instance in the collection to retrieve
* the key
* @return A map of key to value instances
* @throws IllegalArgumentException if any of the other paremeters are invalid.
*/
public static <K, V> Map<K, V> asMap(final java.util.Collection<V> coll,
final Class<K> keyType,
final Class<V> valueType,
final String keyMethodName) {
final HashMap<K, V> map = new HashMap<K, V>();
Method method = null;
if (isEmpty(coll)) return map;
notNull(keyType, Messages.getString(KEY_TYPE_NOT_NULL));
notNull(valueType, Messages.getString(VALUE_TYPE_NOT_NULL));
notEmpty(keyMethodName, Messages.getString(KEY_METHOD_NAME_NOT_NULL));
try {
// return the Method to invoke to get the key for the map
method = valueType.getMethod(keyMethodName);
}
catch (final NoSuchMethodException e) {
final String message =
String.format(
Messages.getString(METHOD_NOT_FOUND),
keyMethodName,
valueType);
e.fillInStackTrace();
logger.error(message, e);
throw new IllegalArgumentException(message, e);
}
try {
for (final V value : coll) {
Object object;
object = method.invoke(value);
@SuppressWarnings("unchecked")
final K key = (K) object;
map.put(key, value);
}
}
catch (final Exception e) {
final String message =
String.format(
Messages.getString(METHOD_CALL_FAILED),
method,
valueType);
e.fillInStackTrace();
logger.error(message, e);
throw new IllegalArgumentException(message, e);
}
return map;
}
回答by cs94njw
I like Kango_V's answer, but I think it's too complex. I think this is simpler - maybe too simple. If inclined, you could replace String with a Generic marker, and make it work for any Key type.
我喜欢 Kango_V 的回答,但我认为它太复杂了。我认为这更简单——也许太简单了。如果愿意,您可以将 String 替换为 Generic 标记,并使其适用于任何 Key 类型。
public static <E> Map<String, E> convertListToMap(Collection<E> sourceList, ListToMapConverterInterface<E> converterInterface) {
Map<String, E> newMap = new HashMap<String, E>();
for( E item : sourceList ) {
newMap.put( converterInterface.getKeyForItem( item ), item );
}
return newMap;
}
public interface ListToMapConverterInterface<E> {
public String getKeyForItem(E item);
}
Used like this:
像这样使用:
Map<String, PricingPlanAttribute> pricingPlanAttributeMap = convertListToMap( pricingPlanAttributeList,
new ListToMapConverterInterface<PricingPlanAttribute>() {
@Override
public String getKeyForItem(PricingPlanAttribute item) {
return item.getFullName();
}
} );
回答by xxf
Universal method
通用方法
public static <K, V> Map<K, V> listAsMap(Collection<V> sourceList, ListToMapConverter<K, V> converter) {
Map<K, V> newMap = new HashMap<K, V>();
for (V item : sourceList) {
newMap.put( converter.getKey(item), item );
}
return newMap;
}
public static interface ListToMapConverter<K, V> {
public K getKey(V item);
}
回答by ripper234
Just in case this question isn't closed as a duplicate, the right answer is to use Google Collections:
以防万一这个问题没有作为重复关闭,正确的答案是使用 Google Collections:
Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, new Function<Role,String>() {
public String apply(Role from) {
return from.getName(); // or something else
}});
回答by Andrejs
There is also a simple way of doing this using Maps.uniqueIndex(...)from Google guavalibraries
还有一种使用Google番石榴库中的Maps.uniqueIndex(...)的简单方法
回答by Alexis C.
With java-8, you'll be able to do this in one line using streams, and the Collectors
class.
使用java-8,您将能够使用流和Collectors
类在一行中完成此操作。
Map<String, Item> map =
list.stream().collect(Collectors.toMap(Item::getKey, item -> item));
Short demo:
简短演示:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test{
public static void main (String [] args){
List<Item> list = IntStream.rangeClosed(1, 4)
.mapToObj(Item::new)
.collect(Collectors.toList()); //[Item [i=1], Item [i=2], Item [i=3], Item [i=4]]
Map<String, Item> map =
list.stream().collect(Collectors.toMap(Item::getKey, item -> item));
map.forEach((k, v) -> System.out.println(k + " => " + v));
}
}
class Item {
private final int i;
public Item(int i){
this.i = i;
}
public String getKey(){
return "Key-"+i;
}
@Override
public String toString() {
return "Item [i=" + i + "]";
}
}
Output:
输出:
Key-1 => Item [i=1]
Key-2 => Item [i=2]
Key-3 => Item [i=3]
Key-4 => Item [i=4]
As noted in comments, you can use Function.identity()
instead of item -> item
, although I find i -> i
rather explicit.
正如评论中所指出的,您可以使用Function.identity()
而不是item -> item
,尽管我发现i -> i
相当明确。
And to be complete note that you can use a binary operator if your function is not bijective. For example let's consider this List
and the mapping function that for an int value, compute the result of it modulo 3:
并且要完整注意,如果您的函数不是双射的,您可以使用二元运算符。例如,让我们考虑这个List
和映射函数,对于一个 int 值,计算它的模 3 的结果:
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6);
Map<String, Integer> map =
intList.stream().collect(toMap(i -> String.valueOf(i % 3), i -> i));
When running this code, you'll get an error saying java.lang.IllegalStateException: Duplicate key 1
. This is because 1 % 3 is the same as 4 % 3 and hence have the same key value given the key mapping function. In this case you can provide a merge operator.
运行此代码时,您会收到一条错误消息,指出java.lang.IllegalStateException: Duplicate key 1
. 这是因为 1 % 3 与 4 % 3 相同,因此在给定键映射函数的情况下具有相同的键值。在这种情况下,您可以提供合并运算符。
Here's one that sum the values; (i1, i2) -> i1 + i2;
that can be replaced with the method reference Integer::sum
.
这是对值求和的一个;(i1, i2) -> i1 + i2;
可以替换为方法引用Integer::sum
。
Map<String, Integer> map =
intList.stream().collect(toMap(i -> String.valueOf(i % 3),
i -> i,
Integer::sum));
which now outputs:
现在输出:
0 => 9 (i.e 3 + 6)
1 => 5 (i.e 1 + 4)
2 => 7 (i.e 2 + 5)
Hope it helps! :)
希望能帮助到你!:)
回答by glts
Since Java 8, the answer by @ZouZouusing the Collectors.toMap
collector is certainly the idiomatic way to solve this problem.
从 Java 8 开始,@ZouZou使用Collectors.toMap
收集器的答案当然是解决这个问题的惯用方法。
And as this is such a common task, we can make it into a static utility.
由于这是一项如此常见的任务,我们可以将其变成一个静态实用程序。
That way the solution truly becomes a one-liner.
这样,解决方案就真正变成了单线解决方案。
/**
* Returns a map where each entry is an item of {@code list} mapped by the
* key produced by applying {@code mapper} to the item.
*
* @param list the list to map
* @param mapper the function to produce the key from a list item
* @return the resulting map
* @throws IllegalStateException on duplicate key
*/
public static <K, T> Map<K, T> toMapBy(List<T> list,
Function<? super T, ? extends K> mapper) {
return list.stream().collect(Collectors.toMap(mapper, Function.identity()));
}
And here's how you would use it on a List<Student>
:
这是您将如何在 a 上使用它的方法List<Student>
:
Map<Long, Student> studentsById = toMapBy(students, Student::getId);