Java 8 List <V>到Map <K,V>


932

我想使用Java 8的流和lambda将对象列表转换为Map。

这就是我在Java 7及以下版本中编写它的方式。

private Map<String, Choice> nameMap(List<Choice> choices) {
        final Map<String, Choice> hashMap = new HashMap<>();
        for (final Choice choice : choices) {
            hashMap.put(choice.getName(), choice);
        }
        return hashMap;
}

我可以使用Java 8和Guava轻松完成此操作,但我想知道如何在没有Guava的情况下执行此操作。

在番石榴:

private Map<String, Choice> nameMap(List<Choice> choices) {
    return Maps.uniqueIndex(choices, new Function<Choice, String>() {

        @Override
        public String apply(final Choice input) {
            return input.getName();
        }
    });
}

带有Java 8 lambda的番石榴。

private Map<String, Choice> nameMap(List<Choice> choices) {
    return Maps.uniqueIndex(choices, Choice::getName);
}

Answers:


1394

根据Collectors文档,它很简单:

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName,
                                              Function.identity()));

167
附带说明一下,即使在Java 8之后,JDK仍然无法参加简短竞赛。番石榴替代看上去非常可读的:Maps.uniqueIndex(choices, Choice::getName)
Bogdan Calmac'3

3
使用(静态进口)SeqJOOL库(我建议你使用Java 8人),还可以改善与简洁: seq(choices).toMap(Choice::getName)
卢肯斯

5
使用Function.identity有什么好处?我的意思是,它->它更短
Shabunc

9
@shabunc我不知道有什么好处,实际上使用了it -> it自己。Function.identity()之所以在这里使用
Lambda,

12
@zapl,呵呵,其实原来也有这背后的原因- stackoverflow.com/questions/28032827/...
shabunc

307

如果不能保证键对于列表中的所有元素都是唯一的,则应将其转换为a Map<String, List<Choice>>而不是aMap<String, Choice>

Map<String, List<Choice>> result =
 choices.stream().collect(Collectors.groupingBy(Choice::getName));

76
实际上,这为您提供了Map <String,List <Choice >>,它处理了非唯一键的可能性,但不是OP所要求的。在番石榴中,Multimaps.index(choices,Choice :: getName)可能是一个更好的选择。
理查德·尼科尔斯

或者更确切地说使用Guava的Multimap <String,Choice>,在相同的键映射到多个值的情况下,它非常方便。Guava中提供了许多实用的方法来使用这样的数据结构,而不是创建Map <String,List <Choice >>
Neeraj

1
@RichardNichols为什么番石榴Multimaps方法是更好的选择?由于它不返回MapObject,因此可能会带来不便。
uv

156

使用getName()的关键和Choice本身作为地图的价值:

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName, c -> c));

19
请写一些描述以便用户理解。
Mayank Jain

4
真的很遗憾,这里没有更多详细信息,因为我最喜欢这个答案。
MadConan 2015年

Collectors.toMap(Choice::getName,c->c)(缩短2个字符)
Ferrybig

7
它等于 choices.stream().collect(Collectors.toMap(choice -> choice.getName(),choice -> choice)); 键的第一功能,值的第二功能
waterscar

16
我知道看到和理解它很容易,c -> c但是Function.identity()携带了更多的语义信息。我通常使用静态导入,以便可以使用identity()
Hank D

28

如果您不想使用Collectors.toMap(),这是另一种方法

Map<String, Choice> result =
   choices.stream().collect(HashMap<String, Choice>::new, 
                           (m, c) -> m.put(c.getName(), c),
                           (m, u) -> {});

1
Collectors.toMap()如上例所示,然后使用哪个更好的方法还是使用我们自己的HashMap?
Swapnil Gangrade

1
此示例提供了有关如何在地图中放置其他内容的示例。我想要一个方法调用未提供的值。谢谢!
th3morg

1
第三个参数函数不正确。在那里,您应该提供一些功能来合并两个Hashmap,例如Hashmap :: putAll
jesantana '17

25

列出的大多数答案都错过了列表中有重复项的情况。在那种情况下,答案就会抛出IllegalStateException。请参考以下代码来处理列表重复项

public Map<String, Choice> convertListToMap(List<Choice> choices) {
    return choices.stream()
        .collect(Collectors.toMap(Choice::getName, choice -> choice,
            (oldValue, newValue) -> newValue));
  }


18

例如,如果要将对象字段转换为映射:

示例对象:

class Item{
        private String code;
        private String name;

        public Item(String code, String name) {
            this.code = code;
            this.name = name;
        }

        //getters and setters
    }

然后将List转换为Map:

List<Item> list = new ArrayList<>();
list.add(new Item("code1", "name1"));
list.add(new Item("code2", "name2"));

Map<String,String> map = list.stream()
     .collect(Collectors.toMap(Item::getCode, Item::getName));


10

我尝试这样做,发现使用上面的答案,当使用Functions.identity()Map的键时,this::localMethodName由于键入问题,我在使用本地方法(如实际工作)时遇到了问题。

Functions.identity()在这种情况下实际上会对类型做一些事情,因此该方法只能通过返回Object并接受参数Object

为了解决这个问题,我最终放弃了Functions.identity()使用s->s

因此,在我的情况下,我的代码列出了目录内的所有目录,并且每个目录都使用目录名作为映射的键,然后使用目录名调用方法并返回项目集合,如下所示:

Map<String, Collection<ItemType>> items = Arrays.stream(itemFilesDir.listFiles(File::isDirectory))
.map(File::getName)
.collect(Collectors.toMap(s->s, this::retrieveBrandItems));

10

您可以使用IntStream创建索引流,然后将它们转换为Map:

Map<Integer,Item> map = 
IntStream.range(0,items.size())
         .boxed()
         .collect(Collectors.toMap (i -> i, i -> items.get(i)));

1
这不是一个好的选择,因为您对每个元素都进行get()调用,因此增加了操作的复杂性(如果项是哈希图,则为o(n * k))。
Nicolas Nobelis

get(i)不在哈希图O(1)上吗?
伊沃·凡德·威肯

@IvovanderVeeken代码段中的get(i)在列表中,而不在地图上。
扎基

@Zaki我在谈论Nicolas的话。如果项是哈希图而不是列表,我看不到n * k的复杂性。
Ivo van der Veeken

8

我将编写如何使用泛型控件反转将列表转换为映射的方法。只是通用的方法!

也许我们有整数列表或对象列表因此,问题如下:地图的关键是什么?

创建界面

public interface KeyFinder<K, E> {
    K getKey(E e);
}

现在使用控制反转:

  static <K, E> Map<K, E> listToMap(List<E> list, KeyFinder<K, E> finder) {
        return  list.stream().collect(Collectors.toMap(e -> finder.getKey(e) , e -> e));
    }

例如,如果我们有book对象,则该类将为地图选择键

public class BookKeyFinder implements KeyFinder<Long, Book> {
    @Override
    public Long getKey(Book e) {
        return e.getPrice()
    }
}

6

我使用这种语法

Map<Integer, List<Choice>> choiceMap = 
choices.stream().collect(Collectors.groupingBy(choice -> choice.getName()));

11
groupingBy创建一个Map<K,List<V>>,而不是一个Map<K,V>
ChristofferHammarström2015年

3
乌里塞斯答案。并且,String getName();(不是整数)
Barett

5
Map<String, Set<String>> collect = Arrays.asList(Locale.getAvailableLocales()).stream().collect(Collectors
                .toMap(l -> l.getDisplayCountry(), l -> Collections.singleton(l.getDisplayLanguage())));

4

这可以通过两种方式完成。让person作为我们将用来演示它的类。

public class Person {

    private String name;
    private int age;

    public String getAge() {
        return age;
    }
}

人员成为要转换到地图的人员列表

1,在列表上使用简单的foreach和Lambda表达式

Map<Integer,List<Person>> mapPersons = new HashMap<>();
persons.forEach(p->mapPersons.put(p.getAge(),p));

2.在给定列表上定义的流上使用收集器。

 Map<Integer,List<Person>> mapPersons = 
           persons.stream().collect(Collectors.groupingBy(Person::getAge));

4

可以使用流来执行此操作。为了消除显式使用的需要Collectors,可以toMap静态导入(如有效Java建议的第三版)。

import static java.util.stream.Collectors.toMap;

private static Map<String, Choice> nameMap(List<Choice> choices) {
    return choices.stream().collect(toMap(Choice::getName, it -> it));
}


3
Map<String,Choice> map=list.stream().collect(Collectors.toMap(Choice::getName, s->s));

甚至为我服务

Map<String,Choice> map=  list1.stream().collect(()-> new HashMap<String,Choice>(), 
            (r,s) -> r.put(s.getString(),s),(r,s) -> r.putAll(s));

3

如果必须覆盖相同键名的每个新值:

public Map < String, Choice > convertListToMap(List < Choice > choices) {
    return choices.stream()
        .collect(Collectors.toMap(Choice::getName,
            Function.identity(),
            (oldValue, newValue) - > newValue));
}

如果必须将所有选项分组在一个名称列表中:

public Map < String, Choice > convertListToMap(List < Choice > choices) {
    return choices.stream().collect(Collectors.groupingBy(Choice::getName));
}

1
List<V> choices; // your list
Map<K,V> result = choices.stream().collect(Collectors.toMap(choice::getKey(),choice));
//assuming class "V" has a method to get the key, this method must handle case of duplicates too and provide a unique key.

0

作为一种替代方法,guava可以使用kotlin-stdlib

private Map<String, Choice> nameMap(List<Choice> choices) {
    return CollectionsKt.associateBy(choices, Choice::getName);
}

-1
String array[] = {"ASDFASDFASDF","AA", "BBB", "CCCC", "DD", "EEDDDAD"};
    List<String> list = Arrays.asList(array);
    Map<Integer, String> map = list.stream()
            .collect(Collectors.toMap(s -> s.length(), s -> s, (x, y) -> {
                System.out.println("Dublicate key" + x);
                return x;
            },()-> new TreeMap<>((s1,s2)->s2.compareTo(s1))));
    System.out.println(map);

双重密钥AA {12 = ASDFASDFASDF,7 = EEDDDAD,4 = CCCC,3 = BBB,2 = AA}


2
您要在这里做什么?你读过这个问题吗?
navderm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.