Combining a Collection of Predicates
We chain a collection of Predicates by reducing them. In the following example, we have a list of predicates that we
combined using Predicate.and()
:
@Test
public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){
List<Predicate<String>> allPredicates = new ArrayList<Predicate<String>>();
allPredicates.add(str -> str.startsWith("A"));
allPredicates.add(str -> str.contains("d"));
allPredicates.add(str -> str.length() > 4);
List<String> result = names.stream()
.filter(allPredicates.stream().reduce(x->true, Predicate::and))
.collect(Collectors.toList());
assertEquals(1, result.size());
assertThat(result, contains("Alexander"));
}
Note that we use our base identity as:
x->true
That will be, however, different if we want to combine them using Predicate.or():
@Test
public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){
List<String> result = names.stream()
.filter(allPredicates.stream().reduce(x->false, Predicate::or))
.collect(Collectors.toList());
assertEquals(2, result.size());
assertThat(result, contains("Adam","Alexander"));
}
Collecting Stream containing "null" to List
person.stream()
.sorted(Comparator.comparingInt(Person::getDriverLicense))
.collect(Collectors.toList());
If this stream contains a kid who doesn't know how to drive, the Collectors.toList()
might see a null
value, which
causes NullPointerException
, because Collectors.toList()
gives us a list implementation that doesn't permit null
elements. ArrayList
, however, permits null
elements according to its JavaDoc. The solution them would be
person.stream()
.sorted(Comparator.comparingInt(Person::getDriverLicense))
.collect(Collectors.toCollection(ArrayList::new));
Note that the last line is changed so that the collect procedure returns an uer-specified list implementation, which is
ArrayList
Sorting
public class User {
private final String name;
private final int age;
}
final List<User> users = Arrays.asList(
new User("C", 30),
new User("D", 40),
new User("A", 10),
new User("B", 20),
new User("E", 50)
);
List<User> sortedList = users.stream()
.sorted(Comparator.comparingInt(User::getAge))
.collect(Collectors.toList());
Remove Duplicates from a List of Objects based on Property
You can get a stream from the List and put in in the TreeSet from which you provide a custom comparator that compares the property uniquely. Then if you really need a list you can put then back this collection into an ArrayList:
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;
...
List<Employee> unique = employee.stream()
.collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(comparingInt(Employee::getId))),
ArrayList::new
)
);
Given the example:
List<Employee> employee = Arrays.asList(new Employee(1, "John"), new Employee(1, "Bob"), new Employee(2, "Alice"));
It will output:
[Employee{id=1, name='John'}, Employee{id=2, name='Alice'}]
Convert Iterable to Stream
StreamSupport.stream(iterable.spliterator(), false)
.filter(...)
.moreStreamOps(...);
Convert Two Dimensional Array to List
List<Foo> collection = Arrays.stream(array) //'array' is two-dimensional
.flatMap(Arrays::stream)
.collect(Collectors.toList());
Preserve Order in Stream with collect
Say we would like to process a list such as ["blah", "blah", "yep"] and get ["blah (2 times)", "yep"], we will collect
them to a LinkedHashMap
to get the expected result:
return messages.stream()
.collect(groupingBy(Function.identity(), LinkedHashMap::new, summingInt(e -> 1)))
.entrySet()
.stream()
.map(e -> e.getKey()+(e.getValue() == 1 ? "" : " (" + e.getValue() +" times)"))
.collect(toList());