Java 8按唯一名称过滤对象列表,同时仅保留最高ID?

sta*_*ack 0 java collections java-8 maxby java-stream

假设我们有一个包含字段的 person 类:

Class Person {
  private String name;
  private Integer id (this one is unique);
}
Run Code Online (Sandbox Code Playgroud)

然后我们有一个List<Person> people这样的:

['Jerry', 993]
['Tom', 3]
['Neal', 443]
['Jerry', 112]
['Shannon', 259]
['Shannon', 533]
Run Code Online (Sandbox Code Playgroud)

我怎样才能创建一个新List<Person> uniqueNames的,使其仅过滤唯一名称并保留该名称的最高 ID。

所以最终列表将如下所示:

['Jerry', 993]
['Tom', 3]
['Neal', 443]
['Shannon', 533]
Run Code Online (Sandbox Code Playgroud)

Ale*_*nko 5

Collectors.groupingBy+Collectors.maxBy应该能够构建按姓名分组的人员地图,然后选择最大值:

List<Person> persons = Arrays.asList(
    new Person("Jerry", 123),
    new Person("Tom", 234),
    new Person("Jerry", 456),
    new Person("Jake", 789)
);

List<Person> maxById = persons
    .stream()
    .collect(Collectors.groupingBy(
        Person::getName, 
        Collectors.maxBy(Comparator.comparingInt(Person::getID))
    ))
    .values() // Collection<Optional<Person>>
    .stream() // Stream<Optional<Person>>
    .map(opt -> opt.orElse(null))
    .collect(Collectors.toList());

System.out.println(maxById);
Run Code Online (Sandbox Code Playgroud)

输出:

[789: Jake, 234: Tom, 456: Jerry]
Run Code Online (Sandbox Code Playgroud)

更新

有没有办法获得因在该stream()中重复而被删除的Person对象的单独列表?

最好将分组的项目收集在一个列表中,然后在某个包装类中进行转换,提供有关人员maxById和已删除重复人员的列表的信息:

class PersonList {
    private final Person max;
    private final List<Person> deduped;
    
    public PersonList(List<Person> group) {
        this.max = Collections.max(group, Comparator.comparingInt(Person::getID));
        this.deduped = new ArrayList<>(group);
        this.deduped.removeIf(p -> p.getID() == max.getID());
    }
    
    @Override
    public String toString() {
        return "{max: " + max + "; deduped: " + deduped + "}";
    }
}
Run Code Online (Sandbox Code Playgroud)

那么这些人应该像这样收集:

List<PersonList> maxByIdDetails = new ArrayList<>(persons
    .stream()
    .collect(Collectors.groupingBy(
        Person::getName, 
        LinkedHashMap::new,
        Collectors.collectingAndThen(
            Collectors.toList(), PersonList::new
        )
    ))
    .values()); // Collection<PersonList>

maxByIdDetails.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

输出:

{max: 456: Jerry; deduped: [123: Jerry]}
{max: 234: Tom; deduped: []}
{max: 789: Jake; deduped: []}
Run Code Online (Sandbox Code Playgroud)

更新2

获取重复人员列表:

{max: 456: Jerry; deduped: [123: Jerry]}
{max: 234: Tom; deduped: []}
{max: 789: Jake; deduped: []}
Run Code Online (Sandbox Code Playgroud)

输出:

[123: Jerry]
Run Code Online (Sandbox Code Playgroud)

其中removeMax可以这样实现:

List<Person> duplicates = persons
    .stream()
    .collect(Collectors.groupingBy(Person::getName))
    .values() // Collection<List<Person>>
    .stream() // Stream<List<Person>>
    .map(MyClass::removeMax)
    .flatMap(List::stream) // Stream<Person>
    .collect(Collectors.toList()); // List<Person>

System.out.println(duplicates);
Run Code Online (Sandbox Code Playgroud)

或者,如果hashCodeequals在类 中正确实现Person,则可以使用以下方法计算两个列表之间的差异removeAll

[123: Jerry]
Run Code Online (Sandbox Code Playgroud)