Filter Java Stream to 1 and only 1 element
Create a custom Collector
public static <T> Collector<T, ?, T> toSingleton() {
return Collectors.collectingAndThen(
Collectors.toList(),
list -> {
if (list.size() != 1) {
throw new IllegalStateException();
}
return list.get(0);
}
);
}
We use Collectors.collectingAndThen
to construct our desired Collector
by
- Collecting our objects in a
List
with theCollectors.toList()
collector. - Applying an extra finisher at the end, that returns the single element — or throws an
IllegalStateException
iflist.size != 1
.
Used as:
User resultUser = users.stream()
.filter(user -> user.getId() > 0)
.collect(toSingleton());
You can then customize this Collector
as much as you want, for example give the exception as argument in the constructor, tweak it to allow two values, and more.
An alternative — arguably less elegant — solution:
You can use a 'workaround' that involves peek()
and an AtomicInteger
, but really you shouldn't be using that.
What you could do instead is just collecting it in a List
, like this:
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);
Java8 filter and return if only element
Not very pretty, but you could limit
the stream to 2 elements, collect
those in a List
, and see if that list has exactly one element. This still has more than one line, and has some overhead for creating the list, but that overhead is limited (to a list of size 2) and it does not have to iterate the stream twice, either.
List<String> tmp = list.stream()
.filter(item -> item.startsWith("A"))
.limit(2)
.collect(Collectors.toList());
Optional<String> res = tmp.size() == 1 ? Optional.of(tmp.get(0)) : Optional.empty();
(My other idea was to use reduce((s1, s2) -> null)
after limit(2)
and reduce any two matches to null
, but instead of returning an Optional.empty
this will just raise an Exception, i.e. it does not work, but maybe this triggers some better (working) ideas.)
Update: It seems like while reduce
raises an Exceptions, Collectors.reducing
does not, and instead returns an Optional.empty
as desired, so this also works, as shown in this answer to a very similar question. Still, I'd add limit(2)
to make it stop early:
Optional<String> res = list.stream()
.filter(item -> item.startsWith("A"))
.limit(2)
.collect(Collectors.reducing((s1, s2) -> null));
(If you like this last part, please upvote the original answer.)
Reduce a collection to a single object of another type with streams
Did you mean :
collection.stream()
.filter(e -> e.key.equals("2"))
.findFirst()
.orElse(null);//Or any default value
You can even throw an exception :
collection.stream()
.filter(e -> e.key.equals("2"))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No data found"));
How to elegantly filter a java stream repeatedly until a single result is found?
You can put all the conditions into a List
and loop over it, applying one filter on each iteration until there is only one element left.
List<Predicate<MyObject>> conditions = List.of(this::firstCondition, this::secondCondition, this::thirdCondition /*...*/ );
for(int i = 0; i < conditions.size() && allCandidates.size() > 1; i++)
allCandidates = allCandidates.stream().filter(conditions.get(i)).toList();
// Note: you may need to check if the list is empty here
return allCandidates;
Is there an idiomatic way to assert a stream has only one element while yielding that element?
If using Guava is palatable to you, use MoreCollectors.onlyElement
:
public Thing thereCanBeOnlyOne(Stream<Thing> stream) {
return stream.collect(MoreCollectors.onlyElement());
}
Equivalents may well exist in other common libraries; I am just familiar with Guava.
How to filter out Distinct objects of a Particular subtype from a List of super type with Streams
You can define a method that would expect an instance of target class Class<T>
in order to filter out any subtype of C
from the source list.
Note that would work only if equals/hashCode
contract was properly implemented by each subclass.
public static <T extends C> List<T> getItemsOfType(List<C> source,
Class<T> itemClass) {
return source.stream()
.filter(item -> itemClass.isAssignableFrom(item.getClass())) // ensures that this element can be safely casted to the target type
.map(itemClass::cast) // casting to the type T
.distinct() // ensures uniqueness of elements according to `equals/hashCode` implementation of the type T
.collect(Collectors.toList());
}
public static void main(String[] args) {
List<C> input = List.of(new A(1), new D(), new A(3));
List<A> result = getItemsOfType(input, A.class);
System.out.println(result);
}
Output:
[A{x=1}, A{x=3}]
Related Topics
Unexpected Type Resulting from the Ternary Operator
Compare Two Objects in Java with Possible Null Values
Integer Arithmetic in Java with Char and Integer Literal
The Concatenation of Chars to Form a String Gives Different Results
Session.Connection() Deprecated on Hibernate
How to Specify Correctly Codebase and Archive in Java Applet
How to Find and Kill Running Win-Processes from Within Java
When Does Hashset 'Add' Method Calls Equals
Collections.Emptylist() VS. New Instance
How Many String Objects Will Be Created
Nextdouble() Throws an Inputmismatchexception When I Enter a Double
How to Sort Arraylist<Long> in Decreasing Order
Java Xml Parser for Huge Files
How to See the Source Code of the Sun Jdk