1012

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

这就是我在 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 的 Guava。

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

22 回答 22

1477

根据Collectors文档,它很简单:

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName,
                                              Function.identity()));
于 2013-12-03T23:30:58.423 回答
341

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

Map<String, List<Choice>> result =
 choices.stream().collect(Collectors.groupingBy(Choice::getName));
于 2014-08-22T18:20:22.430 回答
172

用作地图getName()的键和Choice自身作为地图的值:

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName, c -> c));
于 2015-02-28T07:18:15.950 回答
31

如果您不想使用 Collectors.toMap() 这是另一个

Map<String, Choice> result =
   choices.stream().collect(HashMap<String, Choice>::new, 
                           (m, c) -> m.put(c.getName(), c),
                           (m, u) -> {});
于 2016-04-19T18:38:37.683 回答
29

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

public Map<String, Choice> convertListToMap(List<Choice> choices) {
    return choices.stream()
        .collect(Collectors.toMap(Choice::getName, choice -> choice,
            (oldValue, newValue) -> newValue));
  }
于 2018-08-17T06:45:35.400 回答
22

简单的另一种选择

Map<String,Choice> map = new HashMap<>();
choices.forEach(e->map.put(e.getName(),e));
于 2017-04-07T07:02:40.867 回答
21

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

示例对象:

class Item{
        private String code;
        private String name;

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

        //getters and setters
    }

并操作将列表转换为地图:

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));
于 2017-10-20T13:06:07.477 回答
12

如果您不介意使用 3rd 方库,AOL 的cyclops-react库(披露我是贡献者)具有所有JDK Collection类型的扩展,包括ListMap

ListX<Choices> choices;
Map<String, Choice> map = choices.toMap(c-> c.getName(),c->c);
于 2016-02-24T17:45:07.430 回答
11

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

Map<Integer,Item> map = 
IntStream.range(0,items.size())
         .boxed()
         .collect(Collectors.toMap (i -> i, i -> items.get(i)));
于 2017-06-09T05:33:11.127 回答
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));
于 2016-01-08T10:43:55.010 回答
9

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

也许我们有整数列表或对象列表。所以问题如下:地图的关键应该是什么

创建接口

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()
    }
}
于 2018-07-14T00:29:27.730 回答
7

我使用这种语法

Map<Integer, List<Choice>> choiceMap = 
choices.stream().collect(Collectors.groupingBy(choice -> choice.getName()));
于 2015-04-24T19:31:05.513 回答
5
Map<String, Set<String>> collect = Arrays.asList(Locale.getAvailableLocales()).stream().collect(Collectors
                .toMap(l -> l.getDisplayCountry(), l -> Collections.singleton(l.getDisplayLanguage())));
于 2016-01-24T16:43:51.837 回答
4

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

public class Person {

    private String name;
    private int age;

    public String getAge() {
        return age;
    }
}

people为要转换为地图的 Person 列表

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));
于 2018-06-04T10:45:05.390 回答
4

可以使用流来执行此操作。为了消除显式使用的需要Collectors,可以静态导入toMap(如 Effective 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));
}
于 2018-07-02T12:05:36.907 回答
3

这是StreamEx的解决方案

StreamEx.of(choices).toMap(Choice::getName, c -> c);
于 2017-06-09T03:51:27.903 回答
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));
于 2017-07-12T04:04:24.943 回答
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));
}
于 2019-01-22T18:28:27.380 回答
2

另一种可能只出现在评论中:

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

如果您想使用子对象的参数作为键,这很有用:

Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(c -> c.getUser().getName(), c -> c)));
于 2020-10-21T15:36:20.747 回答
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.
于 2019-08-23T03:16:33.050 回答
0

作为替代方案,guava可以使用kotlin-stdlib

private Map<String, Choice> nameMap(List<Choice> choices) {
    return CollectionsKt.associateBy(choices, Choice::getName);
}
于 2019-11-25T09:55:53.167 回答
-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}
于 2019-01-25T17:08:38.373 回答