Filter Java Stream to 1 and Only 1 Element

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

  1. Collecting our objects in a List with the Collectors.toList() collector.
  2. Applying an extra finisher at the end, that returns the single element — or throws an IllegalStateException if list.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



Leave a reply



Submit