Tuesday, March 21, 2023

Streams in Java

Streams are wrappers around a data source (Collection, array etc.) allowing us to operate on that data source. A stream does not store data and is not a data structure. It also never modifies the underlying data source.

Any operation involving Streams should have three sections - a source, one or more intermediate operations and a terminal operation

 

Source

1. An empty Stream is created as, 

Stream<String> stream = Stream.empty();

2. Any Collection object can be converted to stream using stream() method. 

List<String> lst = Arrays.asList("A", "B", "C", "D");

Stream<String> stream = lst.stream();

Set<String> set = new HashSet<>(list);

Stream<String> strem = set.stream();

3. A Stream can be created from an array in two ways as,

Stream<String> strm = Stream.of("a", "b", "c");

String[] arr = new String[] { "a", "b", "c" };

Stream<String> stream = Arrays.stream(arr);

4. By adding elements individually using the mutable Builder.

Stream.Builder<String> builder = Stream.builder();

Stream<String> stream = builder.add("a").add("b").add("c").build();

5. Create an infinite Stream using Stream.generate() method.

Stream<Double> stream = Stream.generate(Math::random).limit(4);

6. Create an infinite Stream using Stream.iterate()

Stream<Integer> IntegerStream = Stream.iterate(0, n -> n + 1).limit(10);

7. A File can be converted to stream as,

Stream<String> stream = Files.lines(filepath);

8. Using Primitive Streams

IntStream intStream = IntStream.range(1, 3); //1 2

LongStream longStream = LongStream.rangeClosed(1, 3); //1 2 3

9. Stream from a pattern match

Stream<String> stream = Pattern.compile("\\s").splitAsStream("A String");

 

Intermediate Operations

The Streams are further processed by zero or more intermediate methods combined together (pipelined). But actual processing doesn’t start till a terminal operation is invoked. This is called lazy evaluation.

1. filter() to exclude unwanted streams with a condition (known as Predicate)

stream.filter((Employee e)-> e.getAge() > 24)

2. sorted() to arrange in order. A comparator can be passed as parameter.

stream.sorted()

3. distinct() to get non-duplicate elements

stream.distinct()

4. map() to transform the stream using a Function.

stream.map(String::toUpperCase)

5. limit() to restrict the result count

stream.limit(5)

6. peek() to debug every intermediate operation

stream.filter(e -> e.length() > 3).peek(e -> System.out.println("Filtered value: " + e))

 

Terminal Operations

They give the final result. They don't return a stream.

toArray() : to get an array from the stream.

collect() : to convert stream into a Collection. eg: stream.collect(Collectors.toList())

count() : to get the count

reduce() : to get a single result from a sequence. eg: stream().reduce("", String::concat)

forEach() : to do something for each element. eg: stream.forEach(System.out::println)

min() : to get the minimum value based on comparator. Returns an Optional element. 

        eg: Optional<Address> mini = stream.min((i, j) -> i.compareTo(j))

              Address addy = mini.get();

              mini.ispresent(); // False if Optional is empty

max() : to get the maximum value based on comparator. Returns an Optional element.

anyMatch() : True if any element matches, else false. eg: stream.anyMatch(p -> p.getAge() < 20)

allMatch() : True if all elements matche, else false. eg: stream.allMatch(p -> p.getAge() < 20)

noneMatch() : True if no element matches, else false. eg: stream.noneMatch(p -> p.getAge() < 20)

findAny() : Returns Optional with any element.

findFirst() : Returns Optional with the first in order element.

 

Parallel Stream

Parallel Streams are executed parallelly. 

eg: List<Integer> lst = Arrays.asList(1, 2, 3, 4);

int sum = lst.parallelStream().reduce(5, Integer::sum);

Here the result is calculated as (5+1) + (5+2) + (5+3) + (5+4) where as in sequential it would have been 1+2+3+4+15. 

Another way of creating prarallel stream is, stream.parallel().forEach(System.out::println);


No comments: