我有一个这样的课程:
public class Foo {
private String a;
private String b;
private LocalDate c;
private int d;
}
我有一个Foo
个对象列表,我想按a
,b
和c
进行分组,并生成一个地图。这是我到目前为止所做的:
Map<String, List<Foo>> test = foos.stream().collect(Collectors.groupingBy(Foo::getA, Collectors.collectingAndThen(Collectors.groupingBy(Foo::getB), Collections.unmodifiableList())));
但这本身就错了。我不知道如何组合多个字段,但仍然产生Map<String, List<Foo>>
。我有什么想法吗?
编辑1:如果我有以下Foo:
{"Test", "Test", "10/02/2015", 5}
{"Test", "Test", "10/02/2015", 4}
{"Test", "Test", "10/02/2015", 3}
{"Test", "Test", "2/02/2015", 5}
{"Test", "Potato", "2/02/2015", 5}
然后它应分组:
{"Test", "Test", "10/02/2015", [5, 4, 3]}
{"Test", "Test", "2/02/2015", 5}
{"Test", "Potato", "2/02/2015", 5}
我的原始帖子误导了我想要的内容,但基本上它需要按a,b,d分组并生成一个d列表。我知道我可能需要创建一个新类来存储它们:
public class FooResult {
private String a;
private String b;
private LocalDate c;
private List<Integer> d;
}
如何分组并映射到如上所示的新类?
答案 0 :(得分:2)
由于未实现多个字段的组,您必须使用由a
,b
和c
的值组成的复合键。使用该键,可以使用Collector#of()
工厂方法像这样使用收集操作。
Map<String, List<Integer>> result = foos.stream().collect(Collector.of(
HashMap::new,
( map, foo ) -> {
map.compute(foo.a + "_" + foo.b + "_" + foo.c, (key,list) -> {
if(list == null){
list = new ArrayList<>();
}
list.add(foo.d);
return list;
});
},
( map1, map2 ) -> {
map2.forEach(( k, v ) -> {
map1.compute(k, (key, list) -> {
if(list == null){
list = v;
} else {
list.addAll(v);
}
return list;
});
});
return map1;
}
));
答案 1 :(得分:1)
您还可以将中间Press Command+Space and type Terminal and press enter/return key.
Run in Terminal app:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
and press enter/return key. Wait for the command to finish.
Run:
brew install mailutils
与来自Map
类和a
值的字段b
,c
和Foo
汇总的密钥一起使用收集所有List<Integer>
字段值。在下面的示例中,我创建了d
类 - 一个帮助程序类,它聚合这些字段并实现MapKey
和hashCode
方法,因此它可以是用作equals
中的键。
HashMap
然后你可以看到你可以进行转换是6行代码。该程序的输出如下:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FooMain {
public static void main(String[] args) {
final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy");
final List<Foo> foos = Arrays.asList(
new Foo("Test", "Test", LocalDate.parse("10/02/2015", dateFormat), 5),
new Foo("Test", "Test", LocalDate.parse("10/02/2015", dateFormat), 4),
new Foo("Test", "Test", LocalDate.parse("10/02/2015", dateFormat), 3),
new Foo("Test", "Test", LocalDate.parse("02/02/2015", dateFormat), 5),
new Foo("Test", "Potato", LocalDate.parse("02/02/2015", dateFormat), 5)
);
List<FooResult> result = foos.stream()
.collect(Collectors.groupingBy(foo -> new MapKey(foo.a, foo.b, foo.c), Collectors.mapping(Foo::getD, Collectors.toList())))
.entrySet()
.stream()
.map(entry -> new FooResult(entry.getKey().a, entry.getKey().b, entry.getKey().c, entry.getValue()))
.collect(Collectors.toList());
result.forEach(System.out::println);
}
public static final class Foo {
private final String a;
private final String b;
private final LocalDate c;
private final int d;
Foo(String a, String b, LocalDate c, int d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
int getD() {
return d;
}
}
public static final class FooResult {
private final String a;
private final String b;
private final LocalDate c;
private final List<Integer> d;
FooResult(String a, String b, LocalDate c, List<Integer> d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
@Override
public String toString() {
return "FooResult{" +
"a='" + a + '\'' +
", b='" + b + '\'' +
", c=" + c +
", d=" + d +
'}';
}
}
public static final class MapKey {
private final String a;
private final String b;
private final LocalDate c;
MapKey(String a, String b, LocalDate c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MapKey)) return false;
MapKey mapKey = (MapKey) o;
if (a != null ? !a.equals(mapKey.a) : mapKey.a != null) return false;
if (b != null ? !b.equals(mapKey.b) : mapKey.b != null) return false;
return c != null ? c.equals(mapKey.c) : mapKey.c == null;
}
@Override
public int hashCode() {
int result = a != null ? a.hashCode() : 0;
result = 31 * result + (b != null ? b.hashCode() : 0);
result = 31 * result + (c != null ? c.hashCode() : 0);
return result;
}
}
}
我还提出了FooResult{a='Test', b='Potato', c=2015-02-02, d=[5]}
FooResult{a='Test', b='Test', c=2015-02-02, d=[5]}
FooResult{a='Test', b='Test', c=2015-10-02, d=[5, 4, 3]}
,Foo
和FooResult
不可变 - 当你必须处理流转换时,这总是一个不错的选择。您不希望在流操作期间产生任何副作用,并且不可变对象保证这一点。