使用流元素之间的相关性过滤流

时间:2017-11-13 17:52:17

标签: java java-stream reduction

假设有Person类看起来像这样:

public class Person {
    private int id;
    private String discriminator;
    // some other fields plus getters/setters
}

现在我有StreamPerson元素,该流可能包含多个Person个实例,这些实例具有相同的id但不同的discriminator值,即[Person{“id”: 1, “discriminator”: “A”}, Person{“id”: 1, “discriminator”: “B”}, Person{“id”: 2, “discriminator”: “A”}, ...]

我想要做的是过滤掉所有具有某个id的Person个实例,如果至少有一个Person实例具有该ID具有特定的鉴别值。因此,继续上面的例子,通过鉴别器值“A”过滤将产生一个空集合(当然,在还原操作之后)并且通过鉴别器值“B”过滤将产生不包含任何Person实例的集合id等于1。

我知道我可以使用groupingBy收集器和组元素按Person.id减少流,然后如果映射列表包含Map,则从结果Person中删除映射具有指定鉴别值的元素,但我仍然想知道是否有更简单的方法来实现相同的结果?

3 个答案:

答案 0 :(得分:3)

如果我理解你的问题,你会首先找到与鉴别器匹配的所有QIcon

ID

然后删除与这些条目匹配的条目:

Set<Integer> ids = persons.stream()
       .filter(p -> "A".equalsIgnoreCase(p.getDiscriminator()))
       .map(Person::getId)
       .collect(Collectors.toSet())

答案 1 :(得分:1)

Eugenes的答案很有效,但我个人更喜欢单一的陈述。所以我把他的代码整理成一个单独的操作。看起来像这样:

final List<Person> result = persons.stream()
    .filter(p -> "B".equalsIgnoreCase(p.getDiscriminator()))
    .map(Person::getId)
    .collect(
        () -> new ArrayList<>(persons),
        ( list, id ) -> list.removeIf(p -> p.getId() == id),
        ( a, b ) -> {throw new UnsupportedOperationException();}
    );

可能需要提一下,persons的副本是必需的,否则Stream会损坏并遇到null个值。

  

SideNote :当尝试并行使用时,此版本目前正在抛出UnsupportedOperationException

答案 2 :(得分:1)

所以下面我提出了我想出的解决方案。首先,我通过Person.id属性对输入Person集合/流进行分组,然后我对映射条目进行流式处理,并过滤掉至少有一个与给定鉴别器匹配的值的那些。

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        List<Person> persons = Arrays.asList(
            new Person(1, "A"),
            new Person(1, "B"),
            new Person(1, "C"),
            new Person(2, "B"),
            new Person(2, "C"),
            new Person(3, "A"),
            new Person(3, "C"),
            new Person(4, "B")
        );

        System.out.println(persons);
        System.out.println(filterByDiscriminator(persons, "A"));
        System.out.println(filterByDiscriminator(persons, "B"));
        System.out.println(filterByDiscriminator(persons, "C"));
    }

    private static List<Person> filterByDiscriminator(List<Person> input, String discriminator) {
        return input.stream()
            .collect(Collectors.groupingBy(Person::getId))
            .entrySet().stream()
            .filter(entry -> entry.getValue().stream().noneMatch(person -> person.getDiscriminator().equals(discriminator)))
            .flatMap(entry -> entry.getValue().stream())
            .collect(Collectors.toList());
    }

}

class Person {

    private final Integer id;
    private final String discriminator;

    public Person(Integer id, String discriminator) {
        Objects.requireNonNull(id);
        Objects.requireNonNull(discriminator);
        this.id = id;
        this.discriminator = discriminator;
    }

    public Integer getId() {
        return id;
    }

    public String getDiscriminator() {
        return discriminator;
    }

    @Override
    public String toString() {
        return String.format("%s{\"id\": %d, \"discriminator\": \"%s\"}", getClass().getSimpleName(), id, discriminator);
    }
}