我想使用 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);
}
基于 Collectors
documentation,它很简单:
Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(Choice::getName,
Function.identity()));
如果您的键不保证对于列表中的所有元素都是唯一的,则应将其转换为 Map<String, List<Choice>>
而不是 Map<String, Choice>
Map<String, List<Choice>> result =
choices.stream().collect(Collectors.groupingBy(Choice::getName));
Multimaps
方法是更好的选择?这可能会带来不便,因为它不返回 Map
对象。
使用 getName()
作为键,使用 Choice
本身作为映射的值:
Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(Choice::getName, c -> c));
Collectors.toMap(Choice::getName,c->c)
(短 2 个字符)
choices.stream().collect(Collectors.toMap(choice -> choice.getName(),choice -> choice));
键的第一个函数,值的第二个函数
c -> c
是多么容易,但 Function.identity()
包含更多语义信息。我通常使用静态导入,这样我就可以使用 identity()
列出的大多数答案都漏掉了列表中有重复项的情况。在这种情况下,答案将抛出 IllegalStateException
。请参考以下代码处理重复列表:
public Map<String, Choice> convertListToMap(List<Choice> choices) {
return choices.stream()
.collect(Collectors.toMap(Choice::getName, choice -> choice,
(oldValue, newValue) -> newValue));
}
如果您不想使用 Collectors.toMap() 这是另一个
Map<String, Choice> result =
choices.stream().collect(HashMap<String, Choice>::new,
(m, c) -> m.put(c.getName(), c),
(m, u) -> {});
Collectors.toMap()
或我们自己的 HashMap 哪个更好?
简单的另一种选择
Map<String,Choice> map = new HashMap<>();
choices.forEach(e->map.put(e.getName(),e));
例如,如果要将对象字段转换为映射:
示例对象:
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));
如果您不介意使用第 3 方库,AOL 的 cyclops-react 库(披露我是贡献者)具有所有 JDK Collection 类型的扩展,包括 List 和 Map。
ListX<Choices> choices;
Map<String, Choice> map = choices.toMap(c-> c.getName(),c->c);
您可以使用 IntStream 创建索引的 Stream ,然后将它们转换为 Map :
Map<Integer,Item> map =
IntStream.range(0,items.size())
.boxed()
.collect(Collectors.toMap (i -> i, i -> items.get(i)));
我试图这样做并发现,使用上面的答案,当使用 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));
我将编写如何使用泛型和控制反转将列表转换为映射。只是万能的方法!
也许我们有整数列表或对象列表。所以问题如下:地图的关键应该是什么?
创建接口
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()
}
}
我使用这种语法
Map<Integer, List<Choice>> choiceMap =
choices.stream().collect(Collectors.groupingBy(choice -> choice.getName()));
groupingBy
创建的是 Map<K,List<V>>
,而不是 Map<K,V>
。
String getName();
(不是整数)
Map<String, Set<String>> collect = Arrays.asList(Locale.getAvailableLocales()).stream().collect(Collectors
.toMap(l -> l.getDisplayCountry(), l -> Collections.singleton(l.getDisplayLanguage())));
这可以通过两种方式完成。让 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));
可以使用流来执行此操作。要消除显式使用 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));
}
另一种可能只出现在评论中:
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)));
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));
如果必须覆盖相同键名的每个新值:
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));
}
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.
作为 guava
的替代品,可以使用 kotlin-stdlib
private Map<String, Choice> nameMap(List<Choice> choices) {
return CollectionsKt.associateBy(choices, Choice::getName);
}
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}
Maps.uniqueIndex(choices, Choice::getName)
。Seq
(我向任何使用 Java 8 的人推荐),您还可以通过以下方式提高简洁性:seq(choices).toMap(Choice::getName)
it -> it
。此处使用Function.identity()
主要是因为它在参考文档中使用过,这就是我在撰写本文时对 lambdas 的全部了解