Stream API 按名称区分,按值区分最大值

wan*_*Dev 2 java java-stream

给出以下示例类:

class Foo{
    String name;
    int value;
    public Foo(String name, int value) {
        this.name = name;
        this.value = value;
    }
    // getters, setters, toString & equals
}
Run Code Online (Sandbox Code Playgroud)

和 foo 列表:

List<Foo> fooList = List.of(new Foo("A",1), new Foo("A",2),
                                new Foo("B",1),
                                new Foo("C",1),
                                new Foo("D",1), new Foo("D",2), new Foo("D",3)
                            );
Run Code Online (Sandbox Code Playgroud)

我想获得一份按名称区分的 foo 列表,如果有多个,Foos我想保留具有最高值的那个。

我发现这个java-8-distinct-by-property问题可以按名称获取不同列表的元素,它使用此方法来获取不同的元素:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}
Run Code Online (Sandbox Code Playgroud)

我这样使用它:

fooList.stream().filter(distinctByKey(Foo::getName)).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

它可以获取不同的元素,但如果有两个或多个同名元素,则保留列表中的第一个元素,并且我无法添加条件来保留具有最高值的元素。

另一种选择是使用分组依据:

Map<String,List<Foo>> map = fooList.stream().collect(Collectors.groupingBy(f -> f.getName()));
Run Code Online (Sandbox Code Playgroud)

这里我不知道如何从地图中收集具有最高值的Foos到列表中。

使用第一种方法我得到:

Foo{name=A, value=1}
Foo{name=B, value=1}
Foo{name=C, value=1}
Foo{name=D, value=1}
Run Code Online (Sandbox Code Playgroud)

与第二个:

A=[Foo{name=A, value=1}, Foo{name=A, value=2}]
B=[Foo{name=B, value=1}]
C=[Foo{name=C, value=1}]
D=[Foo{name=D, value=1}, Foo{name=D, value=2}, Foo{name=D, value=3}]
Run Code Online (Sandbox Code Playgroud)

我怎样才能获得以下列表:

Foo{name=A, value=2}
Foo{name=B, value=1}
Foo{name=C, value=1}
Foo{name=D, value=3}
Run Code Online (Sandbox Code Playgroud)

Sub*_*mal 7

您可以使用groupBy(classifier,下游)方法。

分类函数将元素映射到某个关键类型 K。下游收集器对类型 T 的元素进行操作并生成类型 D 的结果。生成的收集器生成一个 Map。

片段

fooList.stream().collect(
        Collectors.groupingBy(Foo::getName,
                Collectors.collectingAndThen(
                        Collectors.maxBy(Comparator.comparing(Foo::getValue)),
                        Optional::get
                )
        )
).values().forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

输出

Foo{name='A', value=2}
Foo{name='B', value=1}
Foo{name='C', value=1}
Foo{name='D', value=3}
Run Code Online (Sandbox Code Playgroud)