Site Search:

Collections Streams and Filters

Back>

filter
filter


These new java8 addition to collections are in scope of OCPJP
  • public static <T> void sort(List<T> list, Comparator<? super T> c)
  • default boolean removeIf(Predicate<? super E> filter)
  • default void forEach(Consumer<? super T> action)
  • default void replaceAll(UnaryOperator<E> operator)


OCPJP>cat CollectionsDemo.java 
import java.util.*;

public class CollectionsDemo{
    public static void main(String...args) {
        Collection<String> collection = new ArrayList<>();
        collection.add("Plum");
        collection.add("Apple");
        collection.add("Pineapple");
        collection.add("Grape");
        
        collection.removeIf(i -> i.length() > 5);
        collection.forEach(i -> System.out.print(i + " "));
        System.out.println();
        
        List<String> list = new ArrayList<>(collection);
        list.replaceAll(i -> i.toUpperCase());
        list.forEach(System.out::println);
        
        list.replaceAll(String::toLowerCase);
        System.out.println(list);
        
        list.sort((i, j) -> i.length() - j.length());
        System.out.println(list);
        
        Collections.sort(list, (i, j) -> j.length() - i.length());
        System.out.println(list);
    }
}
OCPJP>javac CollectionsDemo.java 
OCPJP>java CollectionsDemo
Plum Apple Grape 
PLUM
APPLE
GRAPE
[plum, apple, grape]
[plum, apple, grape]
[apple, grape, plum]
OCPJP>


Java 8 added a number of new methods on the Map interface. Only merge() is listed in the OCP objectives. computeIfPresent() and computeIfAbsent() are in upgrade exam objectives.

OCPJP>cat MapDemo.java 
import java.util.*;

public class MapDemo{
    public static void main(String...args) {
        Map<String, String> map = new HashMap<>();
        List<String> list = Arrays.asList("value1", "value2", "value3", "value4");
        list.forEach(i -> map.put("key" + i.substring(i.length() - 1), i));
        System.out.println(map);
        
        System.out.println("=========Map.merge============");
        map.merge("key1", "value1replace", (v1, v2) -> v1 + v2);
        map.merge("key2", "value2replace", (v1, v2) -> v2);
        map.merge("key3", "v2 value does not matter", (v1, v2) -> v1);
        map.merge("key4", "entry is moved, v1, v2 does not matter", (v1, v2) -> null);
        map.merge("key5", "always add me", (v1, v2) -> "missing entry is created, Bifunction does not matter");
        System.out.println(map);
        
        System.out.println("=========Map.computIfPresent============");
        map.computeIfPresent("key2", (k, v) -> k.length() + v.length() + "");
        map.computeIfPresent("nosuchkey", (k, v) -> k + v);
        System.out.println(map);
        
        System.out.println("=========Map.computIfAbsent============");
        map.computeIfAbsent("key2", k -> "key2 is present, this function does not matter");
        map.computeIfAbsent("key6", k -> k + " new value6 added");
        System.out.println(map);
    }
}
OCPJP>javac MapDemo.java 
OCPJP>java MapDemo
{key1=value1, key2=value2, key3=value3, key4=value4}
=========Map.merge============
{key1=value1value1replace, key2=value2replace, key5=always add me, key3=value3}
=========Map.computIfPresent============
{key1=value1value1replace, key2=17, key5=always add me, key3=value3}
=========Map.computIfAbsent============
{key1=value1value1replace, key2=17, key5=always add me, key6=key6 new value6 added, key3=value3}


The above mentioned methods removeIf, forEach are convenient methods for stream operations on collections.

Streams can be obtained in a number of ways. Some examples we need to know for OCPJP include:

Let's take a look at stream source from a collection.
OCPJP>cat StreamSourceDemo.java 
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class StreamSourceDemo{
    public static void main(String...args) {
        
        System.out.println("create stream from a Collection");
        List<Integer> l = new ArrayList<>();
        l.addAll(Arrays.asList(1, 2, 3, 4));
        
        Stream<Integer> stream2 = l.stream();
        stream2.forEach(System.out::print);
        
        System.out.println("\ncan get parallelStream from Collection, notice the order is lost due to parallel");
        l.parallelStream().forEach(System.out::print);
        
        System.out.println("\nCollection's forEach use Stream implicitly");
        l.forEach(System.out::print);
        
        System.out.println("\nCollection's removeIf can be implemented with Stream with filter");
        Predicate<Integer> predicate = i -> i > 2;
        l.stream().filter(predicate.negate()).forEach(System.out::print);
        
        System.out.println("\ncollection.stream().filter don't modify the original collection");
        l.forEach(System.out::print);
        
        System.out.println("\ncollection.removeIf does modify the original collection");
        l.removeIf(predicate);
        l.forEach(System.out::print);
        
        System.out.println("\ncreate stream from Stream.of");
        Stream<String> stream = Stream.of(new String[] {"a", "b", "c", "d"});
        stream.forEach(System.out::print);
        System.out.println();
        stream = Stream.of("a", "b", "c", "d");
        stream.forEach(System.out::print);
        
        System.out.println("\ncreate empty stream Stream.empty");
        stream = Stream.empty();
        System.out.println(stream.count());
        
        System.out.println("\ncreate infinite stream from Stream.iterate");
        Stream<String> stream3 = Stream.iterate("seed", i -> i + " plus");
        stream3.limit(3).forEach(System.out::print);
        
        System.out.println("\ncreate infinite stream from Stream.generate");
        Stream<Double> stream4 = Stream.generate(Math::random);
        System.out.println(stream4.findAny().get());
    }
}
OCPJP>javac StreamSourceDemo.java 
OCPJP>java StreamSourceDemo
create stream from a Collection
1234
can get parallelStream from Collection, notice the order is lost due to parallel
3241
Collection's forEach use Stream implicitly
1234
Collection's removeIf can be implemented with Stream with filter
12
collection.stream().filter don't modify the original collection
1234
collection.removeIf does modify the original collection
12
create stream from Stream.of
abcd
abcd
create empty stream Stream.empty
0

create infinite stream from Stream.iterate
seedseed plusseed plus plus
create infinite stream from Stream.generate
0.5283729433350075


collections are like data containers, we attach a pipe to the container, then we get a data source -- stream (a piece of pipe with water in it) -- stream of water -- stream of things (object) -- stream.

Like a water pipeline, data tap out from stream source can be processed with intermediate operations -- an intermediate operation returns a Stream object, so that another intermediate operation at down stream can tap data from it, the pipeline continues: filter(), distinct(), limit(), skip(), map(), flatMap(), sorted(), peek(). Finally at the other end of the pipeline, we get data objects out of stream with terminal operations -- a terminal operation returns an object other than stream, therefore no data can be tap out from this non-stream object, the pipeline terminated here: count(), min(), max(), findAny(), findFirst(), allMatch(), anyMatch(), noneMatch(), forEach(), reduce(), collect().

The method names are quite intuitive, lets check our guess with an example of stream pipe lines.

OCPJP>cat StreamPipelineDemo.java 
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamPipelineDemo{
    public static void main(String...args) {
        
        System.out.println("========limit -> count============");
        Stream<String> spring = Stream.generate(() -> "water drop");
        //spring.count();  //counting an infinite stream takes for ever
        Stream<String> faucet = spring.limit(10_000);
        long numberOfWaterDrops = faucet.count();
        System.out.println("tapped out " + numberOfWaterDrops + " drops of water from a spring");
        //faucet.count();   // java.lang.IllegalStateException: stream has already been operated upon or closed
        //spring.limit(10_000).distinct().count(); // java.lang.IllegalStateException: stream has already been operated upon or closed
        
        System.out.println("========limit -> distinct -> count============");
        spring = Stream.generate(() -> "water drop");
        long numberOfUniqThing = spring.limit(100_000).distinct().count();
        System.out.println("100_000 drops of water can be represented by " + numberOfUniqThing + " drop of water");
        
        System.out.println("========limit -> forEach============");
        spring = Stream.generate(() -> "water drop");
        List<String> waterContainer = new ArrayList<>();
        spring.limit(10_000).forEach(i -> waterContainer.add(i));
        
        System.out.println("filled the water container with " + waterContainer.size() + " drops of water");
        
        System.out.println("========limit -> forEach============");
        spring = Stream.generate(() -> "sand particle"); //come on, magic spring can spring out anything!
        spring.limit(10_000).forEach(i -> waterContainer.add(i));
        System.out.println("filled the water container with " + waterContainer.size() + " units of water and sand");
        
        System.out.println("========filter -> forEach============");
        Predicate<String> conscious = i -> i.equals("hate") || i.equals("poison");
        Stream.of("salt particle", "salt particle", "spicy", "love", "hate", "poison")
        .filter(conscious.negate())
        .forEach(i -> waterContainer.add(i)); //not that physical anymore
        
        System.out.println("========distinct -> forEach============");
        System.out.println("A barrels of:");
        Stream<String> pipe = waterContainer.stream();
        pipe.distinct().forEach(System.out::println);
        
        System.out.println("========allMatch, nonMatch and anyMatch============");
        pipe = waterContainer.stream();
        boolean isPureWater = pipe.allMatch(i -> i.equals("water drop"));
        //pipe.noneMatch(conscious);   // java.lang.IllegalStateException: stream has already been operated upon or closed
        boolean isOk = waterContainer.stream().noneMatch(conscious);
        boolean isDelicious = waterContainer.stream().anyMatch(i -> i.equals("love"));
        System.out.println("isPureWater: " + isPureWater + " isOk: " + isOk + " isDelicious:" + isDelicious);
        
        System.out.println("========filter -> map -> peek -> findAny============");
        pipe = waterContainer.stream();
        pipe = pipe.filter(i -> !i.equals("sand")); //remove sand
        pipe = pipe.filter(i -> i.equals("water drop")); //pure water
        pipe = pipe.map(i -> "colored " + i); //transform
        pipe = pipe.peek(i -> {if (i.contains("salt")) System.out.println("ah, salty water");});
        pipe = pipe.peek(i -> {if (i.contains("love")) System.out.println("I found love in the water");});
        Optional<String> maybe = pipe.findAny();
        String element = maybe.get();
        System.out.println(element);
        
        System.out.println("========sorted -> findFirst============");
        System.out.println(waterContainer.stream().sorted().findFirst().get());
        
        System.out.println("========skip -> peek -> count============");
        pipe = waterContainer.stream();
        pipe = pipe.skip(20_000); //waste the first 20_000 drops
        pipe = pipe.peek(i -> {if (i.contains("salt")) System.out.println("ah, salty water");});
        pipe = pipe.peek(i -> {if (i.contains("love")) System.out.println("I found love in the water");});
        long count = pipe.count();
        System.out.println("peek cost nothing, still have " + count + " items");
        
        System.out.println("========filter -> sorted -> peek -> count============");
        count = waterContainer.stream()
                .filter(i -> !i.equals("water drop") && !i.equals("sand particle"))
                .sorted()
                .peek(System.out::println)
                .count();
        System.out.println("peek cost nothing, still have " + count + " items");
        
        System.out.println("========filter -> limit -> collect============");
        List<String> cup = waterContainer.stream()
                .filter(i -> i.equals("water drop"))
                .limit(1000)
                .collect(Collectors.toList());
        
        System.out.println("fill a cup with " + cup.stream().count() + " drops of water");
        
        System.out.println("========distinct -> collect============");
        Set<String> recipe = waterContainer.stream()
                .distinct()
                .collect(Collectors.toSet());
        
        System.out.println("funky water recipe:" + recipe + ", enjoy!");
    }
}
OCPJP>
OCPJP>javac StreamPipelineDemo.java 
OCPJP>java StreamPipelineDemo
========limit -> count============
tapped out 10000 drops of water from a spring
========limit -> distinct -> count============
100_000 drops of water can be represented by 1 drop of water
========limit -> forEach============
filled the water container with 10000 drops of water
========limit -> forEach============
filled the water container with 20000 units of water and sand
========filter -> forEach============
========distinct -> forEach============
A barrels of:
water drop
sand particle
salt particle
spicy
love
========allMatch, nonMatch and anyMatch============
isPureWater: false isOk: true isDelicious:true
========filter -> map -> peek -> findAny============
colored water drop
========sorted -> findFirst============
love
========skip -> peek -> count============
ah, salty water
ah, salty water
I found love in the water
peek cost nothing, still have 4 items
========filter -> sorted -> peek -> count============
love
salt particle
salt particle
spicy
peek cost nothing, still have 4 items
========filter -> limit -> collect============
fill a cup with 1000 drops of water
========distinct -> collect============
funky water recipe:[love, water drop, sand particle, salt particle, spicy], enjoy!


We have the first impression of stream pipe lines, which contains a stream source, many intermediate operations and one terminal operation. In the following topics, we study them one by one in depth.