Java 8将<V>列入Map <K,V>

Tom*_*ann 892 java lambda java-8 java-stream

我想使用Java 8的流和lambdas将对象列表转换为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;
}
Run Code Online (Sandbox Code Playgroud)

我可以使用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();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

还有Java 8 lambda的番石榴.

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

zap*_*apl 1345

根据Collectors文档,它很简单:

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

  • 作为旁注,即使在Java 8之后,JDK仍然无法在简洁竞赛中竞争.Guava的替代方案看起来非常易读:`Maps.uniqueIndex(choices,Choice :: getName)`. (161认同)
  • @zapl,哦,实际上事实证明这背后有原因 - /sf/ask/1962297921/ (12认同)
  • @shabunc我不知道有什么好处,实际上我自己也用它了.这里使用`Function.identity()`主要是因为它在引用的文档中使用,而且在写作时我对lambdas的了解都是 (9认同)
  • 使用Function.identity有什么好处吗?我的意思是,它 - >它更短 (5认同)
  • 使用[JOOL](https://github.com/jOOQ/jOOL)库(我建议使用Java 8的任何人)中的(静态导入的)`Seq`,您还可以通过以下方法提高简洁性: (choices).toMap(Choice :: getName)` (3认同)
  • 我在这个实现中遇到了很多问题,一般不推荐它。如果遇到重复的键,这会引发异常。如果你_100%_确定没问题,请使用它,但这给我带来的问题比它解决的问题还要多。在每次抛出异常的情况下(在生产中几次),都可以覆盖现有条目。我总是现在就迭代并在代码审查中标记此用法。 (2认同)
  • @ChristopherSchneider 确实,这里存在一个相当隐藏的异常风险。不确定我是否更喜欢一个默默删除数据的版本,但如果你愿意,`toMap` 有一个 3 参数重载,它需要一个合并函数,例如 `(a,b) -&gt; b` 会给你覆盖你提到的行为。(示例中的`choices.stream().collect(Collectors.toMap(Choice::getName, Function.identity(), (a, b) -&gt; b))`) (2认同)

Uli*_*ses 291

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

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

  • 这实际上为您提供了Map <String,List <Choice >>,它处理非唯一键的可能性,但不是OP请求的.在Guava中,Multimaps.index(选项,Choice :: getName)可能是更好的选择,如果这是你想要的任何方式. (75认同)
  • @RichardNichols,这可能不是OP所要求的,但我正在寻找这个,并且很高兴这个答案存在! (2认同)

Ole*_*Ole 146

使用getName()作为键和选择本身作为映射的值:

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

  • 请写一些描述,以便用户可以理解. (19认同)
  • 我知道看到和理解`c - > c`是多么容易,但``Function.identity()`带有更多的语义信息.我通常使用静态导入,以便我可以使用`identity()` (16认同)
  • 它等于`choices.stream().collect(Collectors.toMap(choice - > choice.getName(),choice - > choice));`第一个函数用于键,第二个函数用于值 (7认同)
  • 这里的细节真的太糟糕了,因为我最喜欢这个答案. (4认同)

Emr*_*lak 22

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

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

  • 第三个参数函数不正确。在那里你应该提供一些函数来合并两个 Hashmap,比如 Hashmap::putAll (2认同)

Sah*_*bra 20

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

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


Ren*_*war 17

还有一个简单方法

Map<String,Choice> map = new HashMap<>();
choices.forEach(e->map.put(e.getName(),e));
Run Code Online (Sandbox Code Playgroud)

  • 使用这种或java 7类型没有可行的区别. (3认同)
  • 我发现这比其他机制更容易阅读和理解发生了什么 (3认同)
  • SO询问Java 8 Streams。 (2认同)

Pio*_*r R 16

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

示例对象:

class Item{
        private String code;
        private String name;

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

        //getters and setters
    }
Run Code Online (Sandbox Code Playgroud)

并将操作转换为List To 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));
Run Code Online (Sandbox Code Playgroud)


Joh*_*ean 10

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

ListX<Choices> choices;
Map<String, Choice> map = choices.toMap(c-> c.getName(),c->c);
Run Code Online (Sandbox Code Playgroud)


iZi*_*ian 8

我试图这样做,并发现,使用上面的答案,当使用Functions.identity()地图的键,然后我有使用本地方法的问题,this::localMethodName因为键入问题实际工作.

Functions.identity()实际上在这种情况下对输入做了一些事情,所以该方法只能通过返回Object并接受一个param来工作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));
Run Code Online (Sandbox Code Playgroud)


L. *_* G. 8

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

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

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

Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(c -> c.getUser().getName(), c -> c)));
Run Code Online (Sandbox Code Playgroud)


use*_*723 7

我使用这种语法

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

  • `groupingBy` 创建一个 `Map&lt;K,List&lt;V&gt;&gt;`,而不是 `Map&lt;K,V&gt;`。 (11认同)
  • 乌利斯的回答。并且,`String getName();`(不是整数) (3认同)

Vik*_*shi 7

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

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

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

Kon*_*ski 7

可以使用流来做到这一点。为了消除显式使用 的需要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));
}
Run Code Online (Sandbox Code Playgroud)


gre*_*rep 6

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

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

创建界面

public interface KeyFinder<K, E> {
    K getKey(E e);
}
Run Code Online (Sandbox Code Playgroud)

现在使用控制反转:

  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));
    }
Run Code Online (Sandbox Code Playgroud)

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

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


Kum*_*hek 5

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


小智 5

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

public class Person {

    private String name;
    private int age;

    public String getAge() {
        return age;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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