我试图使用Java 8流在LinkedList中查找元素。但是,我想保证与筛选条件有且只有一个匹配。

以这段代码为例:

public static void main(String[] args) {

    LinkedList<User> users = new LinkedList<>();
    users.add(new User(1, "User1"));
    users.add(new User(2, "User2"));
    users.add(new User(3, "User3"));

    User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
    System.out.println(match.toString());
}

static class User {

    @Override
    public String toString() {
        return id + " - " + username;
    }

    int id;
    String username;

    public User() {
    }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public int getId() {
        return id;
    }
}

这段代码根据用户的ID查找用户。但是不能保证有多少用户匹配过滤器。

更改过滤器行为:

User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();

将抛出一个NoSuchElementException(很好!)

但是,如果有多个匹配,我希望它抛出一个错误。有办法做到这一点吗?


当前回答

你试过这个吗

long c = users.stream().filter((user) -> user.getId() == 1).count();
if(c > 1){
    throw new IllegalStateException();
}

long count()
Returns the count of elements in this stream. This is a special case of a reduction and is equivalent to:

     return mapToLong(e -> 1L).sum();

This is a terminal operation.

来源:https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

其他回答

Guava有一个名为MoreCollectors.onlyElement()的收集器。

我自己尝试了一个示例代码,这里是解决方案。

User user = Stream.of(new User(2), new User(2), new User(1), new User(2))
            .filter(u -> u.getAge() == 2).findFirst().get();

和用户类

class User {
    private int age;

public User(int age) {
    this.age = age;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
 }
}

你试过这个吗

long c = users.stream().filter((user) -> user.getId() == 1).count();
if(c > 1){
    throw new IllegalStateException();
}

long count()
Returns the count of elements in this stream. This is a special case of a reduction and is equivalent to:

     return mapToLong(e -> 1L).sum();

This is a terminal operation.

来源:https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

另一种选择是使用reduction: (本例使用字符串,但可以轻松应用于包括User在内的任何对象类型)

List<String> list = ImmutableList.of("one", "two", "three", "four", "five", "two");
String match = list.stream().filter("two"::equals).reduce(thereCanBeOnlyOne()).get();
//throws NoSuchElementException if there are no matching elements - "zero"
//throws RuntimeException if duplicates are found - "two"
//otherwise returns the match - "one"
...

//Reduction operator that throws RuntimeException if there are duplicates
private static <T> BinaryOperator<T> thereCanBeOnlyOne()
{
    return (a, b) -> {throw new RuntimeException("Duplicate elements found: " + a + " and " + b);};
}

所以对于User的情况,你会有:

User match = users.stream().filter((user) -> user.getId() < 0).reduce(thereCanBeOnlyOne()).get();

创建一个自定义收集器

public static <T> Collector<T, ?, T> toSingleton() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                if (list.size() != 1) {
                    throw new IllegalStateException();
                }
                return list.get(0);
            }
    );
}

我们使用收集工具。然后构造我们想要的收集器

使用collector. tolist()收集器在列表中收集对象。 在最后应用一个额外的结束符,返回单个元素—或者抛出一个IllegalStateException if列表。Size != 1。

用作:

User resultUser = users.stream()
        .filter(user -> user.getId() > 0)
        .collect(toSingleton());

然后,您可以随心所欲地定制这个Collector,例如在构造函数中将异常作为参数,将其调整为允许两个值,甚至更多。

另一种可能不那么优雅的解决方案是:

您可以使用包含peek()和AtomicInteger的“变通方法”,但实际上不应该使用它。

你可以做的只是把它收集到一个List中,就像这样:

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
List<User> resultUserList = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.toList());
if (resultUserList.size() != 1) {
    throw new IllegalStateException();
}
User resultUser = resultUserList.get(0);