📜 ⬆️ ⬇️

Java Programmer Cheat Sheet 4. Java Stream API



Despite the fact that Java 8 has been released for a long time, not all programmers use its new features, someone stops the fact that work projects are too difficult to translate from Java 7 or even Java 6, use someone in their GWT projects, Someone is doing projects for Android and does not want or can not use third-party libraries to implement lambdas and Stream Api. However, knowledge of lambdas and Stream Api for a Java programmer is often required at interviews, well, it will just be useful when switching to a project that uses Java 8. I would like to offer you a brief cheat sheet on Stream Api with practical examples of implementing various tasks with a new functional approach. Knowledge of lambda and functional programming is not required (I tried to give examples so that everything was clear), the level of the most basic knowledge of Java and above.

Also, since this is a cheat sheet, the article can be used to quickly recall how a particular feature of the Java Stream Api works. A brief listing of the features of the main functions is given at the beginning of the article.
')

For those who do not know what Stream Api is.
Stream API is a new way to work with data structures in a functional style. Most often, using stream in Java 8 works with collections, but in fact this mechanism can be used for a wide variety of data.

Stream Api allows you to write processing of data structures in the style of SQL, if earlier the task to get the sum of all odd numbers from the collection was solved by the following code:
Integer sumOddOld = 0; for(Integer i: collection) { if(i % 2 != 0) { sumOddOld += i; } } 

Then with the help of Stream Api you can solve this problem in a functional style:
  Integer sumOdd = collection.stream().filter(o -> o % 2 != 0).reduce((s1, s2) -> s1 + s2).orElse(0); 

Moreover, Stream Api allows solving the task in parallel only by changing stream () to parallelStream () without any extra code, i.e.
  Integer sumOdd = collection.parallelStream().filter(o -> o % 2 != 0).reduce((s1, s2) -> s1 + s2).orElse(0); 

Already makes the code parallel, without any semaphores, synchronizations, risks of deadlocks, etc.




Let's start from the beginning, namely the creation of stream objects in Java 8.

I. Ways to create streams


Here are some ways to create a stream.
The way to create streamCreation TemplateExample
1. Classic: Creating a stream from the collectioncollection. stream ()
 Collection<String> collection = Arrays.asList("a1", "a2", "a3"); Stream<String> streamFromCollection = collection.stream(); 
2. Creating a stream from valuesStream.of ( value1 , ... valueN )
 Stream<String> streamFromValues = Stream.of("a1", "a2", "a3"); 
3. Creating a stream from an arrayArrays.stream ( array )
 String[] array = {"a1","a2","a3"}; Stream<String> streamFromArrays = Arrays.stream(array); 
4. Creating a stream from a file (each line in the file will be a separate element in the stream)Files.lines ( file_path )
 Stream<String> streamFromFiles = Files.lines(Paths.get("file.txt")) 
5. Creating a stream from a string"line". chars ()
 IntStream streamFromString = "123".chars() 
6. Using Stream.builderStream. builder (). add (...) .... build ()
 Stream.builder().add("a1").add("a2").add("a3").build() 
7. Creating a parallel streamcollection. parallelStream ()
 Stream<String> stream = collection.parallelStream(); 

8. Creating endless streams using Stream.iterate
Stream.iterate ( start_condition , expression_generation )
 Stream<Integer> streamFromIterate = Stream.iterate(1, n -> n + 1) 
9. Creating Endless Stream with Stream.generateStream.generate ( generation_express )
 Stream<String> streamFromGenerate = Stream.generate(() -> "a1") 

In principle, except for the last two ways to create a stream, everything is no different from the usual ways of creating collections. The last two methods are used to generate infinite streams, the initial condition is set in iterate and the expression getting the next value from the previous one, that is, Stream.iterate (1, n -> n + 1) will produce the values ​​1, 2, 3, 4, ... N Stream.generate is used to generate constant and random values, it just gives out the values ​​corresponding to the expression, in this example, it will produce an infinite number of “a1” values.
For those who do not know lambda
The expression n -> n + 1 is just an analogue of the expression Integer func (Integer n) {return n + 1;}, and the expression () -> "a1" is analogous to the expression String func () {return "a1";} wrapped in anonymous class.

More detailed examples
Also this example can be found on github 'e
  System.out.println("Test buildStream start"); //     Stream<String> streamFromValues = Stream.of("a1", "a2", "a3"); System.out.println("streamFromValues = " + streamFromValues.collect(Collectors.toList())); //  streamFromValues = [a1, a2, a3] //     String[] array = {"a1","a2","a3"}; Stream<String> streamFromArrays = Arrays.stream(array); System.out.println("streamFromArrays = " + streamFromArrays.collect(Collectors.toList())); //  streamFromArrays = [a1, a2, a3] Stream<String> streamFromArrays1 = Stream.of(array); System.out.println("streamFromArrays1 = " + streamFromArrays1.collect(Collectors.toList())); //  streamFromArrays = [a1, a2, a3] //     (        ) File file = new File("1.tmp"); file.deleteOnExit(); PrintWriter out = new PrintWriter(file); out.println("a1"); out.println("a2"); out.println("a3"); out.close(); Stream<String> streamFromFiles = Files.lines(Paths.get(file.getAbsolutePath())); System.out.println("streamFromFiles = " + streamFromFiles.collect(Collectors.toList())); //  streamFromFiles = [a1, a2, a3] //     Collection<String> collection = Arrays.asList("a1", "a2", "a3"); Stream<String> streamFromCollection = collection.stream(); System.out.println("streamFromCollection = " + streamFromCollection.collect(Collectors.toList())); //  streamFromCollection = [a1, a2, a3] //      IntStream streamFromString = "123".chars(); System.out.print("streamFromString = "); streamFromString.forEach((e)->System.out.print(e + " , ")); //  streamFromString = 49 , 50 , 51 , System.out.println(); //   Stream.builder Stream.Builder<String> builder = Stream.builder(); Stream<String> streamFromBuilder = builder.add("a1").add("a2").add("a3").build(); System.out.println("streamFromBuilder = " + streamFromBuilder.collect((Collectors.toList()))); //  streamFromFiles = [a1, a2, a3] //    //   Stream.iterate Stream<Integer> streamFromIterate = Stream.iterate(1, n -> n + 2); System.out.println("streamFromIterate = " + streamFromIterate.limit(3).collect(Collectors.toList())); //  streamFromIterate = [1, 3, 5] //   Stream.generate Stream<String> streamFromGenerate = Stream.generate(() -> "a1"); System.out.println("streamFromGenerate = " + streamFromGenerate.limit(3).collect(Collectors.toList())); //  streamFromGenerate = [a1, a1, a1] //    Stream<String> streamEmpty = Stream.empty(); System.out.println("streamEmpty = " + streamEmpty.collect(Collectors.toList())); //  streamEmpty = [] //      Stream<String> parallelStream = collection.parallelStream(); System.out.println("parallelStream = " + parallelStream.collect(Collectors.toList())); //  parallelStream = [a1, a2, a3] 



Ii. Streaming Methods


The Java Stream API offers two kinds of methods:
1. Conveyor - return another stream, that is, they work as a builder,
2. Terminal - return another object, such as a collection, primitives, objects, Optional, etc.

What are the differences between conveyor and terminal methods?
The general rule is that a stream can have as many pipeline calls as possible and at the end one terminal, all pipeline methods are lazily executed and until the terminal method is called no action actually occurs, just like creating a Thread or Runnable object , but do not call it start.

In general, this mechanism is similar to constructing SQL queries, there can be any number of nested Selects and only one result in the end. For example, in the expression collection.stream (). Filter ((s) -> s.contains ("1")). Skip (2) .findFirst (), filter and skip are pipelined, and findFirst is terminal, it returns the object Optional and this ends work with stream.


2.1 Brief description of conveyor methods of working with streams


Stream methodDescriptionExample
filterFilters records, returns only records that match the condition.collection.stream (). filter ("a1" :: equals) .count ()
skipLets skip the first N items.collection.stream (). skip (collection.size () - 1) .findFirst (). orElse ("1")
distinctReturns a stream without duplicates (for the equals method)collection.stream (). distinct (). collect (Collectors.toList ())
mapConverts every element of streamcollection.stream (). map ((s) -> s + "_1"). collect (Collectors.toList ())
peekReturns the same stream, but applies a function to each element of the stream.collection.stream (). map (String :: toUpperCase) .peek ((e) -> System.out.print ("," + e)).
collect (Collectors.toList ())
limitAllows you to limit the sample to a certain number of first elements.collection.stream (). limit (2) .collect (Collectors.toList ())
sortedAllows you to sort the values ​​either in natural order or by specifying the Comparatorcollection.stream (). sorted (). collect (Collectors.toList ())
mapToInt ,
mapToDouble ,
mapToLong
Analogue map, but returns a numeric stream (that is, a stream of numeric primitives)collection.stream (). mapToInt ((s) -> Integer.parseInt (s)). toArray ()
flatmap
flatMapToInt ,
flatMapToDouble ,
flatMapToLong
It looks like a map, but it can create severalcollection.stream (). flatMap ((p) -> Arrays.asList (p.split (",")). stream ()). toArray (String [] :: new)


2.2 Brief description of terminal methods of working with streams


Stream methodDescriptionExample
findFirstReturns the first element from the stream (returns Optional)collection.stream (). findFirst (). orElse ("1")
findAnyReturns any suitable element from the stream (returns Optional)collection.stream (). findAny (). orElse ("1")
collectPresentation of results in the form of collections and other data structurescollection.stream (). filter ((s) -> s.contains ("1")). collect (Collectors.toList ())
countReturns the number of elements in the stream.collection.stream (). filter ("a1" :: equals) .count ()
anyMatchReturns true if the condition is true for at least one element.collection.stream (). anyMatch ("a1" :: equals)
noneMatchReturns true if the condition is not met for one element.collection.stream (). noneMatch ("a8" :: equals)
allMatchReturns true if the condition is true for all items.collection.stream (). allMatch ((s) -> s.contains ("1"))
minReturns the minimum element, uses a comparator as a conditioncollection.stream (). min (String :: compareTo) .get ()
maxReturns the maximum element, uses a comparator as a conditioncollection.stream (). max (String :: compareTo) .get ()
forEachApplies a function to each stream object, order when executed in parallel is not guaranteed.set.stream (). forEach ((p) -> p.append ("_ 1"));
forEachOrderedApplies a function to each stream object, preserving the order of the elements guaranteeslist.stream (). forEachOrdered ((p) -> p.append ("_ new"));
toArrayReturns an array of stream valuescollection.stream (). map (String :: toUpperCase) .toArray (String [] :: new);
reduceAllows you to perform aggregate functions on the entire collection and return a single result.collection.stream (). reduce ((s1, s2) -> s1 + s2) .orElse (0)

Pay attention to the methods findFirst, findAny, anyMatch are short-circuiting methods, that is, bypassing streams is organized in such a way as to find a suitable element as quickly as possible, and not to bypass the entire original stream.

2.3 Brief description of additional methods for numeric streams


Stream methodDescriptionExample
sumReturns the sum of all numbers.collection.stream (). mapToInt ((s) -> Integer.parseInt (s)). sum ()
averageReturns the arithmetic average of all numbers.collection.stream (). mapToInt ((s) -> Integer.parseInt (s)). average ()
mapToObjConverts a numeric stream back to an object.intStream.mapToObj ((id) -> new Key (id)). toArray ()


2.4 Several other useful streaming methods


Stream methodDescription
isParallelFind out if the stream is parallel
parallelReturn a parallel stream, if the stream is already parallel, it can return itself
sequentialReturn a consecutive stream, if the stream is already consistent, it can return itself

With the help of methods parallel and sequential, you can determine which operations can be parallel, and which only sequential. You can also make a parallel stream from any serial stream and vice versa, that is:
 collection.stream(). peek(...). //   parallel(). map(...). //    , sequential(). reduce(...) //    


Attention : it is not recommended to use parallel streams for any long operations (retrieving data from the database, network connections), since all parallel streams work with one fork / join pool and such long operations can stop all parallel streams in the JVM from for the lack of available threads in the pool, i.e. parallel streams should be used only for short operations, where the count goes on for milliseconds, but not for those where the count can go on for seconds and minutes.

Iii. Examples of working with stream methods


Consider working with methods on various tasks that are usually required when working with collections.

3.1 Examples of using filter, findFirst, findAny, skip, limit and count


Condition : a collection of Arrays.asList strings is given ("a1", "a2", "a3", "a1"), let's see how it can be processed using the filter, findFirst, findAny, skip and count methods:
TaskSample codeResult
Return the number of occurrences of the object "a1"collection.stream (). filter ("a1" :: equals) .count ()2
Return the first element of the collection, or 0 if the collection is emptycollection.stream (). findFirst (). orElse ("0")a1
Return the last element of the collection or “empty” if the collection is empty.collection.stream (). skip (collection.size () - 1) .findAny (). orElse ("empty")a1
Find an item in the collection equal to "a3" or throw an errorcollection.stream (). filter ("a3" :: equals) .findFirst (). get ()a3
Return the third element of the collection in ordercollection.stream (). skip (2) .findFirst (). get ()a3
Return two items starting from the secondcollection.stream (). skip (1) .limit (2) .toArray ()[a2, a3]
Select all items by patterncollection.stream (). filter ((s) -> s.contains ("1")). collect (Collectors.toList ())[a1, a1]

Notice that the findFirst and findAny methods return a new type, Optional, introduced in Java 8, in order to avoid a NullPointerException. The filter method is convenient to use to select only a specific set of values, and the skip method allows you to skip a certain number of elements.
If you do not know lambda
The expression "a3" :: equals is an analogue of boolean func (s) {return "a3" .equals (s);}, and (s) -> s.contains ("1") is an analogue of boolean func (s) {return s.contains ("1");} wrapped in an anonymous class.

Condition : a collection of the People class is given (with the fields name — name, age — age, sex — gender), of the form Arrays.asList (new People (“Vasya”, 16, Sex.MAN), new People (“Petya”, 23, Sex.MAN), new People (“Elena”, 42, Sex.WOMEN), new People (“Ivan Ivanovich”, 69, Sex.MAN)). Let's see examples of how to work with this class:

TaskSample codeResult
Select men-conscripts (from 18 to 27 years)peoples.stream (). filter ((p) -> p.getAge ()> = 18 && p.getAge () <27
&& p.getSex () == Sex.MAN) .collect (Collectors.toList ())
[{name = 'Peter', age = 23, sex = MAN}]
Find the average age among menpeoples.stream (). filter ((p) -> p.getSex () == Sex.MAN).
mapToInt (People :: getAge) .average (). getAsDouble ()
36.0
Find the number of potentially working people in the sample (i.e., from 18 years old and considering that women are 55, and men are 60)peoples.stream (). filter ((p) -> p.getAge ()> = 18) .filter (
(p) -> (p.getSex () == Sex.WOMEN && p.getAge () <55) || (p.getSex () == Sex.MAN && p.getAge () <60)). count ()
2

Detailed examples
Also this example can be found on github'e: first class and second class
  // filter -  stream,     ,    // count -      // collect -  stream       // mapToInt -      (,  ) private static void testFilterAndCount() { System.out.println(); System.out.println("Test filter and count start"); Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); Collection<People> peoples = Arrays.asList( new People("", 16, Sex.MAN), new People("", 23, Sex.MAN), new People("", 42, Sex.WOMEN), new People(" ", 69, Sex.MAN) ); //     long count = collection.stream().filter("a1"::equals).count(); System.out.println("count = " + count); //  count = 2 //      List<String> select = collection.stream().filter((s) -> s.contains("1")).collect(Collectors.toList()); System.out.println("select = " + select); //  select = [a1, a1] //  - List<People> militaryService = peoples.stream().filter((p)-> p.getAge() >= 18 && p.getAge() < 27 && p.getSex() == Sex.MAN).collect(Collectors.toList()); System.out.println("militaryService = " + militaryService); //  militaryService = [{name='', age=23, sex=MAN}] //      double manAverageAge = peoples.stream().filter((p) -> p.getSex() == Sex.MAN). mapToInt(People::getAge).average().getAsDouble(); System.out.println("manAverageAge = " + manAverageAge); //  manAverageAge = 36.0 //  -      (..  18        55 ,    60) long peopleHowCanWork = peoples.stream().filter((p) -> p.getAge() >= 18).filter( (p) -> (p.getSex() == Sex.WOMEN && p.getAge() < 55) || (p.getSex() == Sex.MAN && p.getAge() < 60)).count(); System.out.println("peopleHowCanWork = " + peopleHowCanWork); //  manAverageAge = 2 } // findFirst -   Optional    // skip -  N   ( N  ) // collect  stream       private static void testFindFirstSkipCount() { Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); System.out.println("Test findFirst and skip start"); //     String first = collection.stream().findFirst().orElse("1"); System.out.println("first = " + first); //  first = a1 //     String last = collection.stream().skip(collection.size() - 1).findAny().orElse("1"); System.out.println("last = " + last ); //  last = a1 //     String find = collection.stream().filter("a3"::equals).findFirst().get(); System.out.println("find = " + find); //  find = a3 //       String third = collection.stream().skip(2).findFirst().get(); System.out.println("third = " + third); //  third = a3 System.out.println(); System.out.println("Test collect start"); //      List<String> select = collection.stream().filter((s) -> s.contains("1")).collect(Collectors.toList()); System.out.println("select = " + select); //  select = [a1, a1] } //  Limit        private static void testLimit() { System.out.println(); System.out.println("Test limit start"); Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); //     List<String> limit = collection.stream().limit(2).collect(Collectors.toList()); System.out.println("limit = " + limit); //  limit = [a1, a2] //       List<String> fromTo = collection.stream().skip(1).limit(2).collect(Collectors.toList()); System.out.println("fromTo = " + fromTo); //  fromTo = [a2, a3] //     String last = collection.stream().skip(collection.size() - 1).findAny().orElse("1"); System.out.println("last = " + last ); //  last = a1 } private enum Sex { MAN, WOMEN } private static class People { private final String name; private final Integer age; private final Sex sex; public People(String name, Integer age, Sex sex) { this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public Integer getAge() { return age; } public Sex getSex() { return sex; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; } } 



3.2 Examples of using distinct


The distinct method returns stream without duplicates, while for an ordered stream (for example, a list-based collection), the order is stable, for an unordered stream, order is not guaranteed. Consider the results of the collection on the Collection ordered ordered = Arrays.asList ("a1", "a2", "a2", "a3", "a1", "a2", "a2") and Collection nonOrdered = new HashSet <> (ordered ).
TaskSample codeResult
Getting a collection without duplicates from an unordered streamnonOrdered.stream (). distinct (). collect (Collectors.toList ())[a1, a2, a3] -
order is not guaranteed
Getting a collection without duplicates from streamlined streamordered.stream (). distinct (). collect (Collectors.toList ());[a1, a2, a3] -
order is guaranteed


Note:
1. If you use distinct with a class that has equals redefined, you must also correctly override hashCode according to the equals / hashCode contract (the most important thing is that hashCode for all equals objects returns the same value), otherwise the distinct may not remove duplicates (similarly like using HashSet / HashMap),
2. If you use parallel streams and you don’t care about the order of elements after removing duplicates - it’s much better for performance to do first stream unordered with unordered (), and only then use distinct (), since maintaining sorting stability while parallel streaming is rather expensive. resources and distinct () on an ordered stream will be executed much longer than with an unordered,

Detailed examples
Also this example can be found on github'e
 //  distinct  stream  ,      (,    list)   ,   -    //  collect  stream       private static void testDistinct() { System.out.println(); System.out.println("Test distinct start"); Collection<String> ordered = Arrays.asList("a1", "a2", "a2", "a3", "a1", "a2", "a2"); Collection<String> nonOrdered = new HashSet<>(ordered); //     List<String> distinct = nonOrdered.stream().distinct().collect(Collectors.toList()); System.out.println("distinct = " + distinct); //  distinct = [a1, a2, a3] -    List<String> distinctOrdered = ordered.stream().distinct().collect(Collectors.toList()); System.out.println("distinctOrdered = " + distinctOrdered); //  distinct = [a1, a2, a3] -   } 



3.3 Examples of using Match functions (anyMatch, allMatch, noneMatch)


Condition : a collection of Arrays.asList strings is given ("a1", "a2", "a3", "a1"), let's see how it can be processed using the Match function

TaskSample codeResult
Find whether there is at least one "a1" element in the collection.collection.stream (). anyMatch ("a1" :: equals)true
Find whether there is at least one “a8” item in the collection.collection.stream (). anyMatch ("a8" :: equals)false
Find whether there is a symbol "1" for all elements of the collection.collection.stream (). allMatch ((s) -> s.contains ("1"))false
Check that there are no “a7” items in the collection.collection.stream (). noneMatch ("a7" :: equals)true

Detailed examples
Also this example can be found on github'e
  //  anyMatch -  true,         //  noneMatch -  true,         //  allMatch -  true,       private static void testMatch() { System.out.println(); System.out.println("Test anyMatch, allMatch, noneMatch start"); Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); //           boolean isAnyOneTrue = collection.stream().anyMatch("a1"::equals); System.out.println("anyOneTrue " + isAnyOneTrue); //  true boolean isAnyOneFalse = collection.stream().anyMatch("a8"::equals); System.out.println("anyOneFlase " + isAnyOneFalse); //  false //          boolean isAll = collection.stream().allMatch((s) -> s.contains("1")); System.out.println("isAll " + isAll); //  false //    boolean isNotEquals = collection.stream().noneMatch("a7"::equals); System.out.println("isNotEquals " + isNotEquals); //  true } 



3.4 Examples of using Map functions (map, mapToInt, FlatMap, FlatMapToInt)


Condition : two collections, collection1 = Arrays.asList ("a1", "a2", "a3", "a1") and collection2 = Arrays.asList ("1.2.0", "4.5"), are given Let's see how it can be processed using various map functions.

TaskSample codeResult
Add "_1" to each element of the first collectioncollection1.stream (). map ((s) -> s + "_1"). collect (Collectors.toList ())[a1_1, a2_1, a3_1, a1_1]
In the first collection, remove the first character and return an array of numbers (int [])collection1.stream (). mapToInt ((s) -> Integer.parseInt (s.substring (1))). toArray ()[1, 2, 3, 1]
From the second collection get all the numbers listed, separated by commas from all the elements.collection2.stream (). flatMap ((p) -> Arrays.asList (p.split (",")). stream ()). toArray (String [] :: new)[1, 2, 0, 4, 5]
From the second collection to get the sum of all the numbers separated by commascollection2.stream (). flatMapToInt ((p) -> Arrays.asList (p.split (",")). stream (). mapToInt (Integer :: parseInt)). sum ()12

Please note: all map functions can return an object of another type (class), that is, map can work with a stream of lines, and output Stream from Integer values ​​or get the People class, and return the Office class, where these people work, etc. p., flatMap (flatMapToInt, etc.) should return a stream with one, several or no elements for each element of the incoming stream (see the last two examples) for output.

Detailed examples
Also this example can be found on github'e
  //  Map     ,  stream    private static void testMap() { System.out.println(); System.out.println("Test map start"); Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); //     List<String> transform = collection.stream().map((s) -> s + "_1").collect(Collectors.toList()); System.out.println("transform = " + transform); //  transform = [a1_1, a2_1, a3_1, a1_1] //       List<Integer> number = collection.stream().map((s) -> Integer.parseInt(s.substring(1))).collect(Collectors.toList()); System.out.println("number = " + number); //  transform = [1, 2, 3, 1] } //  MapToInt -     ,  stream     private static void testMapToInt() { System.out.println(); System.out.println("Test mapToInt start"); Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); //       int[] number = collection.stream().mapToInt((s) -> Integer.parseInt(s.substring(1))).toArray(); System.out.println("number = " + Arrays.toString(number)); //  number = [1, 2, 3, 1] } //  FlatMap -   Map -    ,    stream  private static void testFlatMap() { System.out.println(); System.out.println("Test flat map start"); Collection<String> collection = Arrays.asList("1,2,0", "4,5"); //    ,      collection String[] number = collection.stream().flatMap((p) -> Arrays.asList(p.split(",")).stream()).toArray(String[]::new); System.out.println("number = " + Arrays.toString(number)); //  number = [1, 2, 0, 4, 5] } //  FlatMapToInt -   MapToInt -    ,    stream  private static void testFlatMapToInt() { System.out.println(); System.out.println("Test flat map start"); Collection<String> collection = Arrays.asList("1,2,0", "4,5"); //     ,      collection int sum = collection.stream().flatMapToInt((p) -> Arrays.asList(p.split(",")).stream().mapToInt(Integer::parseInt)).sum(); System.out.println("sum = " + sum); //  sum = 12 } 



3.5 Examples of using the Sorted function


Condition : two collections of strings Arrays.asList ("a1", "a4", "a3", "a2", "a1", "a4") and a collection of people of the class People are given (with fields name - name, age - age , sex - gender), of the type of Arrays.asList (new People (“Vasya”, 16, Sex.MAN), new People (“Peter”, 23, Sex.MAN), new People (“Elena”, 42, Sex. WOMEN), new People (“Ivan Ivanovich”, 69, Sex.MAN)). Let's see examples of how to sort them:

TaskSample codeResult
Sort the string collection alphabeticallycollection.stream (). sorted (). collect (Collectors.toList ())[a1, a1, a2, a3, a4, a4]
Sort the string collection alphabetically in reverse order.collection.stream (). sorted ((o1, o2) -> -o1.compareTo (o2)). collect (Collectors.toList ())[a4, a4, a3, a2, a1, a1]
Sort the collection of strings alphabetically and remove duplicatescollection.stream().sorted().distinct().collect(Collectors.toList())[a1, a2, a3, a4]
collection.stream().sorted((o1, o2) -> -o1.compareTo(o2)).distinct().collect(Collectors.toList())[a4, a3, a2, a1]
peoples.stream().sorted((o1,o2) -> -o1.getName().compareTo(o2.getName())).collect(Collectors.toList())[{''}, {' '}, {''}, {''}]
,peoples.stream().sorted((o1, o2) -> o1.getSex() != o2.getSex()? o1.getSex().
compareTo(o2.getSex()): o1.getAge().compareTo(o2.getAge())).collect(Collectors.toList())
[{'Vasya'}, {'Petya'}, {'Ivan Ivanovich'}, {'Elena'}]


Detailed examples
Also this example can be found on github'e
  //  Sorted       ,   Comparator private static void testSorted() { System.out.println(); System.out.println("Test sorted start"); // ************    Collection<String> collection = Arrays.asList("a1", "a4", "a3", "a2", "a1", "a4"); //     List<String> sorted = collection.stream().sorted().collect(Collectors.toList()); System.out.println("sorted = " + sorted); //  sorted = [a1, a1, a2, a3, a4, a4] //        List<String> sortedDistinct = collection.stream().sorted().distinct().collect(Collectors.toList()); System.out.println("sortedDistinct = " + sortedDistinct); //  sortedDistinct = [a1, a2, a3, a4] //        List<String> sortedReverse = collection.stream().sorted((o1, o2) -> -o1.compareTo(o2)).collect(Collectors.toList()); System.out.println("sortedReverse = " + sortedReverse); //  sortedReverse = [a4, a4, a3, a2, a1, a1] //           List<String> distinctReverse = collection.stream().sorted((o1, o2) -> -o1.compareTo(o2)).distinct().collect(Collectors.toList()); System.out.println("distinctReverse = " + distinctReverse); //  sortedReverse = [a4, a3, a2, a1] // ************    //    Collection<People> peoples = Arrays.asList( new People("", 16, Sex.MAN), new People("", 23, Sex.MAN), new People("", 42, Sex.WOMEN), new People(" ", 69, Sex.MAN) ); //        Collection<People> byName = peoples.stream().sorted((o1,o2) -> -o1.getName().compareTo(o2.getName())).collect(Collectors.toList()); System.out.println("byName = " + byName); // byName = [{name='', age=23, sex=MAN}, {name=' ', age=69, sex=MAN}, {name='', age=42, sex=WOMEN}, {name='', age=16, sex=MAN}] //    ,     Collection<People> bySexAndAge = peoples.stream().sorted((o1, o2) -> o1.getSex() != o2.getSex() ? o1.getSex(). compareTo(o2.getSex()) : o1.getAge().compareTo(o2.getAge())).collect(Collectors.toList()); System.out.println("bySexAndAge = " + bySexAndAge); // bySexAndAge = [{name='', age=16, sex=MAN}, {name='', age=23, sex=MAN}, {name=' ', age=69, sex=MAN}, {name='', age=42, sex=WOMEN}] } private enum Sex { MAN, WOMEN } private static class People { private final String name; private final Integer age; private final Sex sex; public People(String name, Integer age, Sex sex) { this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public Integer getAge() { return age; } public Sex getSex() { return sex; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof People)) return false; People people = (People) o; return Objects.equals(name, people.name) && Objects.equals(age, people.age) && Objects.equals(sex, people.sex); } @Override public int hashCode() { return Objects.hash(name, age, sex); } } 



3.6 Examples of using Max and Min functions


Condition : a collection of Arrays.asList strings is given ("a1", "a2", "a3", "a1"), and a collection of the Peoples class from past examples of the Sorted and Filter functions.

TaskSample codeResult
Find the maximum value among the row collectioncollection.stream (). max (String :: compareTo) .get ()a3
Find the minimum value among the row collectioncollection.stream (). min (String :: compareTo) .get ()a1
Find a person with a maximum age.peoples.stream (). max ((p1, p2) -> p1.getAge (). compareTo (p2.getAge ())). get (){name = 'Ivan Ivanovich', age = 69, sex = MAN}
Find a person with a minimum agepeoples.stream (). min ((p1, p2) -> p1.getAge (). compareTo (p2.getAge ())). get (){name = 'Vasya', age = 16, sex = MAN}


Detailed examples
Also this example can be found on github'e
  //  max   ,      //  min   ,      private static void testMinMax() { System.out.println(); System.out.println("Test min and max start"); // ************    Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); //    String max = collection.stream().max(String::compareTo).get(); System.out.println("max " + max); //  a3 //    String min = collection.stream().min(String::compareTo).get(); System.out.println("min " + min); //  a1 // ************     //    Collection<People> peoples = Arrays.asList( new People("", 16, Sex.MAN), new People("", 23, Sex.MAN), new People("", 42, Sex.WOMEN), new People(" ", 69, Sex.MAN) ); //      People older = peoples.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge())).get(); System.out.println("older " + older); //  {name=' ', age=69, sex=MAN} //      People younger = peoples.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge())).get(); System.out.println("younger " + younger); //  {name='', age=16, sex=MAN} } private enum Sex { MAN, WOMEN } private static class People { private final String name; private final Integer age; private final Sex sex; public People(String name, Integer age, Sex sex) { this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public Integer getAge() { return age; } public Sex getSex() { return sex; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof People)) return false; People people = (People) o; return Objects.equals(name, people.name) && Objects.equals(age, people.age) && Objects.equals(sex, people.sex); } @Override public int hashCode() { return Objects.hash(name, age, sex); } } 



3.7 Examples of using ForEach and Peek functions


Both ForEach and Peek essentially do the same thing, change the properties of objects in the stream, the only difference between them is that ForEach is terminal and it finishes working with the stream while Peek is pipelining and work with the stream continues. For example, there is a collection:
  Collection<StringBuilder> list = Arrays.asList(new StringBuilder("a1"), new StringBuilder("a2"), new StringBuilder("a3")); 

And you need to add "_new" to each element, then for ForEach the code will be
  list.stream().forEachOrdered((p) -> p.append("_new")); // list -  [a1_new, a2_new, a3_new] 

and for peek the code will be
  List<StringBuilder> newList = list.stream().peek((p) -> p.append("_new")).collect(Collectors.toList()); //  list  newList  [a1_new, a2_new, a3_new] 


Detailed examples
Also this example can be found on github'e
  //  ForEach             private static void testForEach() { System.out.println(); System.out.println("For each start"); Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); //        System.out.print("forEach = "); collection.stream().map(String::toUpperCase).forEach((e) -> System.out.print(e + ",")); //  forEach = A1,A2,A3,A1, System.out.println(); Collection<StringBuilder> list = Arrays.asList(new StringBuilder("a1"), new StringBuilder("a2"), new StringBuilder("a3")); list.stream().forEachOrdered((p) -> p.append("_new")); System.out.println("forEachOrdered = " + list); //  forEachOrdered = [a1_new, a2_new, a3_new] } //  Peek    ,           private static void testPeek() { System.out.println(); System.out.println("Test peek start"); Collection<String> collection = Arrays.asList("a1", "a2", "a3", "a1"); //        System.out.print("peak1 = "); List<String> peek = collection.stream().map(String::toUpperCase).peek((e) -> System.out.print(e + ",")). collect(Collectors.toList()); System.out.println(); //  peak1 = A1,A2,A3,A1, System.out.println("peek2 = " + peek); //  peek2 = [A1, A2, A3, A1] Collection<StringBuilder> list = Arrays.asList(new StringBuilder("a1"), new StringBuilder("a2"), new StringBuilder("a3")); List<StringBuilder> newList = list.stream().peek((p) -> p.append("_new")).collect(Collectors.toList()); System.out.println("newList = " + newList); //  newList = [a1_new, a2_new, a3_new] } 



3.8 Examples of using the Reduce function


The reduce method allows you to perform aggregate functions on the entire collection (such as the sum, finding the minimum or maximum value, etc.), it returns one value for the stream, the function takes two arguments — the value obtained in the previous steps and the current value.

Condition : Given a collection of numbers Arrays.asList (1, 2, 3, 4, 2) perform several actions on them using reduce.

TaskSample codeResult
Get the sum of numbers or return 0collection.stream (). reduce ((s1, s2) -> s1 + s2) .orElse (0)12
Return maximum or -1collection.stream (). reduce (Integer :: max) .orElse (-1)four
Return the sum of odd numbers or 0collection.stream (). filter (o -> o% 2! = 0) .reduce ((s1, s2) -> s1 + s2) .orElse (0)four


Detailed examples
github'e
  //  reduce        (  ,       ..) //    Optional  // map -      (,     ) // mapToInt -      (,    int) private static void testReduce() { System.out.println(); System.out.println("Test reduce start"); // ************     Collection<Integer> collection = Arrays.asList(1, 2, 3, 4, 2); //   Integer sum = collection.stream().reduce((s1, s2) -> s1 + s2).orElse(0); //  stream Api Integer sumOld = 0; //    for(Integer i: collection) { sumOld += i; } System.out.println("sum = " + sum + " : " + sumOld); //  sum = 12 : 12 //   Integer max1 = collection.stream().reduce((s1, s2) -> s1 > s2 ? s1 : s2).orElse(0); //  stream Api Integer max2 = collection.stream().reduce(Integer::max).orElse(0); //  stream Api  Integer::max Integer maxOld = null; //    for(Integer i: collection) { maxOld = maxOld != null && maxOld > i? maxOld: i; } maxOld = maxOld == null? 0 : maxOld; System.out.println("max = " + max1 + " : " + max2 + " : " + maxOld); //  max1 = 4 : 4 : 4 //   Integer min = collection.stream().reduce((s1, s2) -> s1 < s2 ? s1 : s2).orElse(0); //  stream Api Integer minOld = null; //    for(Integer i: collection) { minOld = minOld != null && minOld < i? minOld: i; } minOld = minOld == null? 0 : minOld; System.out.println("min = " + min+ " : " + minOld); //  min = 1 : 1 //    Integer last = collection.stream().reduce((s1, s2) -> s2).orElse(0); //  stream Api Integer lastOld = null; //    for(Integer i: collection) { lastOld = i; } lastOld = lastOld == null? 0 : lastOld; System.out.println("last = " + last + " : " + lastOld); //  last = 2 : 2 //   ,   2 Integer sumMore2 = collection.stream().filter(o -> o > 2).reduce((s1, s2) -> s1 + s2).orElse(0); //  stream Api Integer sumMore2Old = 0; //    for(Integer i: collection) { if(i > 2) { sumMore2Old += i; } } System.out.println("sumMore2 = " + sumMore2 + " : " + sumMore2Old); //  sumMore2 = 7 : 7 //     Integer sumOdd = collection.stream().filter(o -> o % 2 != 0).reduce((s1, s2) -> s1 + s2).orElse(0); //  stream Api Integer sumOddOld = 0; //    for(Integer i: collection) { if(i % 2 != 0) { sumOddOld += i; } } System.out.println("sumOdd = " + sumOdd + " : " + sumOddOld); //  sumOdd = 4 : 4 // ************     //    Collection<People> peoples = Arrays.asList( new People("", 16, Sex.MAN), new People("", 23, Sex.MAN), new People("", 42, Sex.WOMEN), new People(" ", 69, Sex.MAN) ); //     int oldMan = peoples.stream().filter((p) -> p.getSex() == Sex.MAN).map(People::getAge).reduce((s1, s2) -> s1 > s2 ? s1 : s2).get(); System.out.println("oldMan = " + oldMan); //  69 //             int younger = peoples.stream().filter((p) -> p.getName().contains("")).mapToInt(People::getAge).reduce((s1, s2) -> s1 < s2 ? s1 : s2).orElse(0); System.out.println("younger = " + younger); //  23 } private enum Sex { MAN, WOMEN } private static class People { private final String name; private final Integer age; private final Sex sex; public People(String name, Integer age, Sex sex) { this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public Integer getAge() { return age; } public Sex getSex() { return sex; } @Override public String toString() { return "{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof People)) return false; People people = (People) o; return Objects.equals(name, people.name) && Objects.equals(age, people.age) && Objects.equals(sex, people.sex); } @Override public int hashCode() { return Objects.hash(name, age, sex); } } 



3.9 Examples of using the toArray and collect functions


If everything is simple with toArray, you can either call toArray () to get Object [], or toArray (T [] :: new) - by getting an array of type T, then collect allows you to convert many values ​​into a collection, map or any other type . To do this, use static methods from Collectors, for example, the conversion to List will be stream.collect (Collectors.toList ()).

Let's look at static methods from Collectors:
MethodDescription
toList, toCollection, toSetrepresent stream in the form of a list, collection or set
toConcurrentMap, toMaplet you convert stream to map
averagingInt, averagingDouble, averagingLongreturn average
summingInt, summingDouble, summingLongreturns amount
summarizingInt, summarizingDouble, summarizingLongreturn SummaryStatistics with different aggregate values
partitioningBysplits the collection into two parts according to the condition and returns them as Map <Boolean, List>
groupingBysplits the collection into several parts and returns Map <N, List <T >>
mappingadditional value conversions for complex collectors


Now let's look at working with collect and toArray with examples:
Condition: Given the collection of numbers Arrays.asList (1, 2, 3, 4), consider the work of collect and toArray with it
TaskSample codeResult
Get the sum of odd numbersnumbers.stream (). collect (Collectors.summingInt (((p) -> p% 2 == 1? p: 0)))four
Subtract 1 from each item and get the averagenumbers.stream (). collect (Collectors.averagingInt ((p) -> p - 1))1.5
Add to the numbers 3 and get statisticsnumbers.stream (). collect (Collectors.summarizingInt ((p) -> p + 3))IntSummaryStatistics {count = 4, sum = 22, min = 4, average = 5.5, max = 7}
Divide numbers into even and oddnumbers.stream (). collect (Collectors.partitioningBy ((p) -> p% 2 == 0)){false = [1, 3], true = [2, 4]}


Condition: Given a collection of Arrays.asList strings ("a1", "b2", "c3", "a1"), consider the work of collect and toArray with it
TaskSample codeResult
Getting a list without duplicatesstrings.stream (). distinct (). collect (Collectors.toList ())[a1, b2, c3]
Get an array of strings without duplicates and in upper casestrings.stream (). distinct (). map (String :: toUpperCase) .toArray (String [] :: new){A1, B2, C3}
Combine all elements in one line through the separator: and wrap tags <b> ... </ b>strings.stream (). collect (Collectors.joining (":", "<b>", "</ b>"))<b> a1: b2: c3: a1 </ b>
map, ,strings.stream().distinct().collect(Collectors.toMap((p) -> p.substring(0, 1), (p) -> p.substring(1, 2))){a=1, b=2, c=3}
map,strings.stream().collect(Collectors.groupingBy((p) -> p.substring(0, 1))){a=[a1, a1], b=[b2], c=[c3]}
map, :strings.stream().collect(Collectors.groupingBy((p) -> p.substring(0, 1), Collectors.mapping((p) -> p.substring(1, 2), Collectors.joining(":")))){a=1:1, b=2, c=3}


github'e
  //  collect  stream       //     Collectors: // toList, toCollection, toSet -     ,    // toConcurrentMap, toMap -     map,    // averagingInt, averagingDouble, averagingLong -    // summingInt, summingDouble, summingLong -   // summarizingInt, summarizingDouble, summarizingLong -  SummaryStatistics     // partitioningBy -             Map<Boolean, List> // groupingBy -       Map<N, List<T>>,  T -   , N -   // mapping -      Collector' private static void testCollect() { System.out.println(); System.out.println("Test distinct start"); // ********    Collection<String> strings = Arrays.asList("a1", "b2", "c3", "a1"); //        List<String> distinct = strings.stream().distinct().collect(Collectors.toList()); System.out.println("distinct = " + distinct); //  distinct = [a1, b2, c3] //        String[] array = strings.stream().distinct().map(String::toUpperCase).toArray(String[]::new); System.out.println("array = " + Arrays.asList(array)); //  array = [A1, B2, C3] //         :    <b> ... </b> String join = strings.stream().collect(Collectors.joining(" : ", "<b> ", " </b>")); System.out.println("join = " + join); //  <b> a1 : b2 : c3 : a1 </b> //   map,    ,    Map<String, String> map = strings.stream().distinct().collect(Collectors.toMap((p) -> p.substring(0, 1), (p) -> p.substring(1, 2))); System.out.println("map = " + map); //  map = {a=1, b=2, c=3} //   map,      Map<String, List<String>> groups = strings.stream().collect(Collectors.groupingBy((p) -> p.substring(0, 1))); System.out.println("groups = " + groups); //  groups = {a=[a1, a1], b=[b2], c=[c3]} //   map,               : Map<String, String> groupJoin = strings.stream().collect(Collectors.groupingBy((p) -> p.substring(0, 1), Collectors.mapping((p) -> p.substring(1, 2), Collectors.joining(":")))); System.out.println("groupJoin = " + groupJoin); //  groupJoin = groupJoin = {a=1/1, b=2, c=3} // ********    Collection<Integer> numbers = Arrays.asList(1, 2, 3, 4); //     long sumOdd = numbers.stream().collect(Collectors.summingInt(((p) -> p % 2 == 1 ? p : 0))); System.out.println("sumOdd = " + sumOdd); //  sumEven = 4 //     1    double average = numbers.stream().collect(Collectors.averagingInt((p) -> p - 1)); System.out.println("average = " + average); //  average = 1.5 //    3    IntSummaryStatistics statistics = numbers.stream().collect(Collectors.summarizingInt((p) -> p + 3)); System.out.println("statistics = " + statistics); //  statistics = IntSummaryStatistics{count=4, sum=22, min=4, average=5.500000, max=7} //      IntSummaryStatistics long sumEven = numbers.stream().collect(Collectors.summarizingInt((p) -> p % 2 == 0 ? p : 0)).getSum(); System.out.println("sumEven = " + sumEven); //  sumEven = 6 //       Map<Boolean, List<Integer>> parts = numbers.stream().collect(Collectors.partitioningBy((p) -> p % 2 == 0)); System.out.println("parts = " + parts); //  parts = {false=[1, 3], true=[2, 4]} } 



3.10 Collector'a


In addition to the Collectors already defined in Collectors, you can also create your own Collector. Let's consider an example of how to create it.

Method for defining a custom Collector:

 Collector<_, _, _> ollector = Collector.of( __, ___, ___, [___] ); 


As can be seen from the code above, for the implementation of your Collector, you need to define three or four methods (method_of the last_processing_accumulator is not necessary). Consider the following code, which we wrote before Java 8, to combine all the rows in the collection:

  StringBuilder b = new StringBuilder(); // __ for(String s: strings) { b.append(s).append(" , "); // ___, } String joinBuilderOld = b.toString(); // ___ 


And similar code to be written in Java 8

 String joinBuilder = strings.stream().collect( Collector.of( StringBuilder::new, // __ (b ,s) -> b.append(s).append(" , "), // ___, (b1, b2) -> b1.append(b2).append(" , "), // ___ StringBuilder::toString // ___ ) ); 


In general, the three methods are easy to understand from the code above, we wrote them practically at every processing of collections, but what is the connection_two_accumulator method? This is the method that is needed for parallel processing of Collector, in this case, when a parallel stream is used, the collection can be divided into two parts (or more parts), each of which will have its own StringBuilder battery and then it will be necessary to combine them, then the code to Java 8 with 2 threads it will be like this:

  StringBuilder b1 = new StringBuilder(); // ___1 for(String s: stringsPart1) { // stringsPart1 -    strings b1.append(s).append(" , "); // ___, } StringBuilder b2 = new StringBuilder(); // ___2 for(String s: stringsPart2) { // stringsPart2 -    strings b2.append(s).append(" , "); // ___, } StringBuilder b = b1.append(b2).append(" , "), // ___ String joinBuilderOld = b.toString(); // ___ 


Let's write your analog Collectors.toList () to work with string stream:
  //    toList Collector<String, List<String>, List<String>> toList = Collector.of( ArrayList::new, //    List::add, //     (l1, l2) -> { l1.addAll(l2); return l1; } //        ); //           List<String> distinct1 = strings.stream().distinct().collect(toList); 


Detailed examples
github'e
  //   Collector,        StringBuilder Collector<String,StringBuilder, String> stringBuilderCollector = Collector.of( StringBuilder::new, //    (b ,s) -> b.append(s).append(" , "), //     (b1, b2) -> b1.append(b2).append(" , "), //        StringBuilder::toString // ,     ); String joinBuilder = strings.stream().collect(stringBuilderCollector); System.out.println("joinBuilder = " + joinBuilder); //  joinBuilder = a1 , b2 , c3 , a1 , //  Collector'   JDK7   StringBuilder b = new StringBuilder(); //    for(String s: strings) { b.append(s).append(" , "); //     } String joinBuilderOld = b.toString(); // ,     System.out.println("joinBuilderOld = " + joinBuilderOld); //  joinBuilderOld = a1 , b2 , c3 , a1 , //    toList         Collector<String, List<String>, List<String>> toList = Collector.of( ArrayList::new, //    List::add, //     (l1, l2) -> { l1.addAll(l2); return l1; } //        ); List<String> distinct1 = strings.stream().distinct().collect(toList); System.out.println("distinct1 = " + distinct1); //  distinct1 = [a1, b2, c3] 


Iv. Conclusion


That's all.I hope my small cheat sheet for working with stream api was useful for you. All source code is on github , good luck writing good code.

PS List of other articles where you can read more about Stream Api:
1. Processing Data with Java SE 8 Streams, Part 1 from Oracle,
2. Processing Data with Java SE 8 Streams, Part 2 from Oracle,
3. Complete Java 8 manual Stream

PPS I also advise you to look at my opensource project useful-java-links - perhaps the most comprehensive collection of useful Java libraries, frameworks and Russian-language instructional videos.There is also a similar English version of this project and I’m starting the opensource sub-project of Hello world to prepare a collection of simple examples for different Java libraries in one maven project (I will be grateful for any help).

Source: https://habr.com/ru/post/270383/


All Articles