使用java流,如何从一个列表创建一个Map来索引同一个类上的2个键?
我在这里给予了一个代码示例,我想Map“personByName”获得所有人的名字或姓氏,所以我想得到3个“史蒂夫”:当它是他们的名字或姓氏。我不知道如何混合2 Collectors.groupingBy。
public static class Person {
final String firstName;
final String lastName;
protected Person(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
@Test
public void testStream() {
List<Person> persons = Arrays.asList(
new Person("Bill", "Gates"),
new Person("Bill", "Steve"),
new Person("Steve", "Jobs"),
new Person("Steve", "Wozniac"));
Map<String, Set<Person>> personByFirstName = persons.stream().collect(Collectors.groupingBy(Person::getFirstName, Collectors.toSet()));
Map<String, Set<Person>> personByLastName = persons.stream().collect(Collectors.groupingBy(Person::getLastName, Collectors.toSet()));
Map<String, Set<Person>> personByName = persons.stream().collect(Collectors.groupingBy(Person::getLastName, Collectors.toSet()));// This is wrong, I want bot first and last name
Assert.assertEquals("we should search by firstName AND lastName", 3, personByName.get("Steve").size()); // This fails
}
我通过在2个Map上循环找到了一个变通方案,但它不是面向流的。
7条答案
按热度按时间8ljdwjyq1#
你可以这样做:
假设您向
Person
类添加了一个toString()
方法,则可以使用以下命令查看结果:r1wp621o2#
例如,您可以合并两个
Map<String, Set<Person>>
tkqqtvp13#
一种方法是使用最新的JDK12的
Collector.teeing
:Collectors.teeing
收集到两个独立的收集器,然后将结果合并为一个最终值。返回由两个下游收集器组合而成的收集器。传递到结果收集器的每个元素都由这两个下游收集器处理,然后使用指定的合并函数将其结果合并为最终结果。
因此,上面的代码按名字收集到一个Map,也按姓氏收集到一个Map,然后通过迭代
byLast
Map并通过Map.computeIfAbsent
方法将其每个条目合并到byFirst
Map中,从而将两个Map合并为最终Map。注意,为了简化示例,我将集合Map到了
Map<String, List<Person>>
而不是Map<String, Set<Person>>
,如果你真的需要一个集合Map,你可以这样做:请记住,如果需要将
Set<Person>
作为Map的值,则Person
类必须实现hashCode
和equals
方法 * 一致 *。6gpjuf904#
如果您想要一个真实的的面向流的解决方案,请确保不生成任何大型中间集合,否则流的大部分意义将丢失。
如果您只想过滤所有史蒂夫,请先过滤,后收集:
如果你想用一个流元素做一些复杂的事情,例如把一个元素放到多个集合中,或者放在一个Map中的几个键下,只需使用
forEach
消费一个流,并在其中写入你想要的任何处理逻辑。kcwpcxri5#
不能按多个值设置Map的键。对于要实现的目标,有三个选项:
1.将“personByFirstName”和“personByLastName”Map结合起来,您将得到重复的值(例如,Bill Gates将出现在
Bill
键下的Map中,也将出现在Gates
键下的Map中)。@Andreas的答案给出了一个很好的基于流的方法来做到这一点。1.使用像lucene这样的索引库,按名字和姓氏索引所有Person对象。
1.流方法-它在大型数据集上性能不佳,但您可以对集合进行流处理,并使用
filter
来获取匹配项:(我已经根据记忆编写了语法,所以您可能需要调整它)。
t9aqgxwy6#
如果我没弄错的话,你需要Map每个Person两次,一次是名字,一次是姓氏。要做到这一点,你必须加倍你的流。假设Couple是某个现有的2元组(Guava或Vavr有一些很好的实现),你可以:
我没有测试过,但概念是:为每个人创建一个(name,person),(surname,person)...的流,然后简单地Map每对夫妇的左边值。asList将有一个集合作为值。如果你需要一个Set chenge最后一行
.collect(Collectors.toMap(Couple::getLeft, c -> new HashSet(c.getRight), Collection::addAll))
wpx232ag7#
尝试
SetMultimap
,从Google Guava或我的库abacus-common