In part1 of these series we saw basics of java stream.Now we discuss how to use the streams along with some important operations on it. Now let's see different ways to create streams.
The below example creates an infinite stream of natural numbers starting with 1.
generate an infinite stream of random numbers.Here we use method reference to generate random numbers.Please follow the series method reference( double colon perator) if you are not aware about it.
Example
A map operation applies a function to each element of the input stream to produce another stream ( output stream ).The number of elements in the input and output streams are same. So this is a one to one mapping.The above figure shows the mapping.It take the element e1 and apply function f on it to get f(e1) and so on.But the type of elements in the output stream may be different from the type of elements in the input stream.Let's take an example.
Suppose we have 1000 keys with values in redis data store and we want to fetch all the values of those keys and then we will perform some operation on them.We want to do it with future object,So how will we do it parallely with java Stream.We will use thread pool service here to fetch the data from redis.Suppose our uniqueItemids List contains the list of keys.
mapped stream will be Stream<Stream<R>> But which is not desired.Assume we have a map with below structure.
A immediate solution comes to mind is to write like below.
itemsMap.values().stream().parallel().map(m->m.values().stream()).forEach(System.out::println);
Now we get the output as follows
java.util.stream.ReferencePipeline$Head@4eec7777
java.util.stream.ReferencePipeline$Head@3b07d329
We are expected to see list of strings in the output.But we don't find that.This is because of inside the map Stream of String is produced and we give Stream<Stream<String>> as the input to foreach.So we get the result.
Now our next attempt like this
[abc, cde, def, rty]
[2abc, 2cde, 2def, 2rty]
[3abc, 3cde, 3def, 3rty]
[abc3, cde3, def3, rty3]
We are able to find all our Strings together but observe that they are still Stream<Stream<String>> .Just we managed to write it in a different way in the for each loop.
The correct approach to our problem is
We will discuss some other important operation in series 3.
Create streams from existing values:
There are two methods in stream interface to create stream from a single value and multiple values.
Stream stream = Stream.of("test");
Stream stream = Stream.of("test1", "test2", "test3", "test4");
Create empty stream :
Stream stream = Stream.empty();
Create Stream from function:
We can generate an infinite stream from a function that can produce infinite number of elements if required.There are two static methods iterate and generate in Stream interface to produce infinite stream.
Stream iterate(T seed, UnaryOperator f)
Stream generate(Supplier s);
The iterator() method takes two arguments: a seed and a function. The first argument is a seed that is the first element of the stream. The second element is generated by applying the function to the first element. The third element is generated by applying the function on the second element and so on.The below example creates an infinite stream of natural numbers starting with 1.
Stream<Integer> naturalNumbers = Stream.iterate(1, n -> n + 1);
The generate(Supplier<T> s) method uses the specified Supplier to generate an infinite sequential unordered stream.Here Supplier is a functional interface, so we can use lambda expressions here.Lets see the below example togenerate an infinite stream of random numbers.Here we use method reference to generate random numbers.Please follow the series method reference( double colon perator) if you are not aware about it.
Stream.generate(Math::random).limit(5).forEach(System.out::println);
Create Stream from Collections:
Collection is the data-source we usually use for creating streams.The Collection interface contains the stream() and parallelStream() methods that create sequential and parallel streams from a Collection.Example
Set nameSet = new HashSet<>();
//add some elements to the set
nameSet.add("name1");
nameSet.add("tes2");
//create a sequential stream from the nameSet
Stream sequentialStream = nameSet.stream();
// Create a parallel stream from the nameSet
Stream parallelStream = nameSet.parallelStream();
Create Streams from Files:
Many methods are added to classes in java.io and java.nio.file package in java 8 to facilitate IO operations by using streams.Let's see the example to read the content of the file using stream.
Path path = Paths.get(filePath);
Stream lines = Files.lines(path);
lines.forEach(System.out::println);
the method lines() added in Files class in java 1.8.Read all lines from a file as a Stream.
Stream Operations:
Now we will go through with some commonly used stream operations and their usage.- Distinct
- filter
- flatMap
- limit
- map
- skip
- peek
- sorted
- allMatch
- anyMatch
- findAny
- findFirst
- noneMatch
- forEach
- reduce
Map Operation:
A map operation applies a function to each element of the input stream to produce another stream ( output stream ).The number of elements in the input and output streams are same. So this is a one to one mapping.The above figure shows the mapping.It take the element e1 and apply function f on it to get f(e1) and so on.But the type of elements in the output stream may be different from the type of elements in the input stream.Let's take an example.
Suppose we have 1000 keys with values in redis data store and we want to fetch all the values of those keys and then we will perform some operation on them.We want to do it with future object,So how will we do it parallely with java Stream.We will use thread pool service here to fetch the data from redis.Suppose our uniqueItemids List contains the list of keys.
HashOperations redisHash=redisTemplate.opsForHash();
ExecutorService threadPoolService=Executors.newFixedThreadPool(10);
uniqueItemIds.
stream().
parallel().
map(itemId-> threadPoolService.submit(new Callable()) .forEach(future->{
try {
return future.get();
} catch (Exception e) {
e.printStackTrace();
return null;
}
Here the code in the callable's call method will be to fetch the data from redis with the specified item id.As we know the submit will return us the future object ,so map operation here takes an itemid which is of type long and return us an object of type future.Here I am emphasizing the point that "the type of elements in the output stream returned by the map operation may be different from the type of elements in the input stream"
flatMap Operation:
Unlike the map operation ,the Streams API supports one-to-many mapping through the flatMap.The mapping function takes an element from the input stream and maps the element to a stream. The type of input element and the elements in the mapped stream may be different.This step produces a stream of streams.If the input stream is a Stream<T> then themapped stream will be Stream<Stream<R>> But which is not desired.Assume we have a map with below structure.
Map>> itemsMap = new ConcurrentHashMap<>()
//Now let's fill the map with some values.
itemsMap.put(2, new ConcurrentHashMap<>());
itemsMap.put(3, new ConcurrentHashMap<>());
itemsMap.get(2).put(1L, Arrays.asList("abc","cde","def","rty"));
itemsMap.get(2).put(2L, Arrays.asList("2abc","2cde","2def","2rty"));
itemsMap.get(2).put(3L, Arrays.asList("3abc","3cde","3def","3rty"));
itemsMap.get(3).put(1L, Arrays.asList("abc3","cde3","def3","rty3"));
Now our aim is to get all the lists of strings in a stream.How can we achieve it?A immediate solution comes to mind is to write like below.
itemsMap.values().stream().parallel().map(m->m.values().stream()).forEach(System.out::println);
Now we get the output as follows
java.util.stream.ReferencePipeline$Head@4eec7777
java.util.stream.ReferencePipeline$Head@3b07d329
We are expected to see list of strings in the output.But we don't find that.This is because of inside the map Stream of String is produced and we give Stream<Stream<String>> as the input to foreach.So we get the result.
Now our next attempt like this
itemsMap.
values().
stream().
parallel().
map(m->m.values().
stream()).
forEach(e->e.forEach(System.out::println));
And the output is :[abc, cde, def, rty]
[2abc, 2cde, 2def, 2rty]
[3abc, 3cde, 3def, 3rty]
[abc3, cde3, def3, rty3]
We are able to find all our Strings together but observe that they are still Stream<Stream<String>> .Just we managed to write it in a different way in the for each loop.
The correct approach to our problem is
itemsMap.
values().
stream().
parallel().
flatMap(m->m.values().
stream()).
forEach(System.out::println);
So here comes the flatMap to the rescue.It flattens the Stream<Stream<String>> and convert it into Stream<String> .So make sure to use flatMap when you get Stream<Stream<T>>
We will discuss some other important operation in series 3.
No comments:
Post a Comment