favorite
|
I am trying to use Java 8 Stream s to find elements in a LinkedList . I want to guarantee, however, that there is 1 and only 1 match to the filter criteria.
Take this code:
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; } }
This code finds a User based on their ID. But there are no guarantees how many User s matched the filter.
Changing the filter line to:
User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();
Will throw a NoSuchElementException (good!)
I would like it to throw an error if there are multiple matches, though. Is there a way to do this?
java lambda stream java-8
|
|
|
count() is a terminal operation so you can’t do that. The stream can’t be used after. – ZouZou Mar 27 at 17:42 |
|
Ok, thanks @ZouZou. I wasn’t entirely certain what that method did. Why is there noStream::size ? – ryvantage Mar 27 at 17:44 |
|
@ryvantage Because a stream can only be used once: calculating its size means "iterating" over it and after that you can’t use the stream any longer. – assylias Mar 27 at 17:45 |
|
Wow. That one comment helped me understand Stream s so much more than I did before… – ryvantage Mar 27 at 17:50 |
activeoldestvotes
|
|
|
@ryvantage I updated my answer with how I would do it with writing the own custom Collector, which I believe is the correct method. – skiwi Mar 27 at 18:03 |
|
what do you think of my reduction? – assylias Mar 27 at 18:32 |
|
@assylias I commented on your answer, I think this one i smore concise and more straight forward though and less error prone. – skiwi Mar 27 at 18:38 |
|
The difference main between the two collectors is that the first will stop as soon as it finds a second matching item whereas the second will iterate through the whole list. – assylias Mar 27 at 22:57 |
Louis Wasserman’s, +1), but if you want brevity, I’d suggest the following:
List<User> result = users.stream() .filter(user -> user.getId() == 1) .limit(2) .collect(Collectors.toList());
Then verify the size of the result list.
|
|
|
What’s the point of limit(2) in this solution? What difference would it make whether the resulting list was 2 or 100? If it’s greater than 1. – ryvantage Mar 28 at 18:31 |
|
It stops immediately if it finds a second match. This is what all the fancy collectors do, just using more code. :-) – Stuart Marks Mar 29 at 3:24 |
share|improve this answer |
answered Mar 27 at 17:51
Louis Wasserman
71k894155
|
|
share|improve this answer |
edited Oct 3 at 17:32
David Conrad
3,8651220
|
answered May 24 at 19:26
Brian Goetz
8,86341733
|
|
share|improve this answer |
edited Mar 27 at 18:31
|
answered Mar 27 at 17:37
assylias
110k10153289
|
|
|
I would add an identity element (null ) to prevent using get() . Sadly your reduce is not working as you think it does, consider a Stream that has null elements in it, maybe you think that you covered it, but I can be [User#1, null, User#2, null, User#3] , now it will not throw an exception I think, unless I’m mistaken here. – skiwiMar 27 at 18:36 |
|
@Skiwi if there are null elements the filter will throw a NPE first. – assylias Mar 27 at 18:37 |
share|improve this answer |
answered Mar 28 at 7:03
pardeep131085
826134
|
|
add a comment |