New features of JDK8 -- Part 2 -- Stream

catalogue

4.1 brief introduction

4.2} components of Stream

4.2.1 data source

4.2.2 intermediate operation

4.2.2.1 filter

4.2.2.2 map

4.2.2.3 distinct

4.2.2.4 sort

4.2.2.5 flatMap

4.2.3 termination

4.2.3.1 forEach

4.2.3.2 allMatch && anyMatch && noneMatch

4.2.3.3 findFirst and findAny search,

4.2.3.4 max, min and count, count()

4.2.3.5 reduce reduction (convert elements in Stream stream into a value)

4.2.3.6 collect (convert elements in Stream stream into a container)

4.2.3.6.1 accept three parameters

4.2.3.6.2. Accept Collector interface

4.1 brief introduction

  • The previous lamdba and functional interfaces are for the preparation of Stream

    • Functional interface function < T, R > -- > map in stream

    • filter in functional interface predict < T > -- > stream

  • The use of Stream makes our code more concise and easy to understand (easy to maintain). Its use reduces a large number of if conditional statements and for loop statements. From input to output, it is like a river, making the maintainer read more like reading an article.

4.2} components of Stream

  • A Stream is mainly composed of three parts: data source, intermediate operation and termination operation.

  • Note:

    • stream does not store values, but obtains values through pipes

    • Instead of changing the original object, they return a new stream that holds the result.

    • stream operations are delayed, which means they wait until the results are needed (filtering, mapping, sorting)

  • Lazy evaluation (intermediate operation): the intermediate operation will not be executed until the operation execution is terminated. xxxx().yyyy().zzzzz()

  • Evaluate early (terminate operation)

4.2.1 data source

  • Common flow creation methods

    • Stream.of, we can create a stream by passing in a generic array or multiple parameters through the static method of stream.

    • Arrays.stream, we can pass in a generic array through the static method of arrays to create a stream.

    • Collection.stream, you can use the default method of the set interface to create a stream; Using this method includes

    • Collection interface, such as Set, List, Map, etc.

    • Generate Stream stream from file

4.2.2 intermediate operation

Common intermediate operations, such as list Take stream () data source as an example

The stateful intermediate operations have to wait until the stateless intermediate operations above have been completed. The unified operation is here

4.2.2.1 filter

  • Filter the qualified elements in the flow. After list stream(). The operation of filter (item - > item > 0) leaves only item elements greater than 0 in the flow.

4.2.2.2 map

  • Element type conversion. Integer set list, through list stream(). Map (item - > item. Tostring()), and each element in the flow is converted to String type.

4.2.2.3 distinct

  • De duplication of elements in the stream. After list stream(). Distinct() operation, the repeated elements in the stream are killed. In particular, distinct de duplication is based on hashCode and equals methods. If the element is an object and it is required to de duplicate according to an attribute of the object, the hashCode and equals methods need to be rewritten.

//Label management function module Allow users to add labels in batches. The background needs to de duplicate the labels and prevent the existence of labels with the same name in the database
tagListFromDB:List of tag names that exist in the database{Li Si,Wang Wu,Zhao Liu}
tagListFromReq:User submitted{(Zhang San,10),(Li Si,30),(Zhang San,10)}
//distinct: use the equa method to compare reference objects
//filter: the final result is an object that is true
tagListFromReq.stream.filter(tag ->!tagListFromDB.containsKey(tag.getname)).distinct.
foreach(tag -> system.out.println(tag))

4.2.2.4 sort

  • Sort the elements in the stream. After list stream(). Sort() operation, the elements in the stream are sorted naturally. If the list composed of student objects is required to be in reverse order according to the age of students, i.e. list stream(). sorted(Comparator.comparing(Student::getAge). Reversed ()), and each element sorted with sort must implement the Comparable interface.

//In stocks, the principle of matching trading is to apply for trading within a period of time. The higher the price, the first transaction; If the price is the same, the earliest time to place an order shall be the first to clinch a deal;
//If the price is consistent with the time and the transaction volume is large, the transaction shall be concluded first; If the price, time and trading volume are consistent, institutions will give priority to the transaction and retail investors will make the final transaction

trades.stream().sorted(
	Comparator
		//Priority should be given to sorting according to the price (NATURAL sorting is from small to large), and there should be an inverse operation
		.comparing(
			Trade::getPrice,
			Comparator.reverseOrder())
		//Sort by time
		.thenComparing(Trade::getTime)
		//Sort by quantity
		.thenComparing(
			Trade::getCount,
			Comparator.reverseOrder())
		.thenComparing(
			Trade::getType,
			(type1,type2) -> {
				if("mechanism".equals(type1) && "Retail investors".equals(type2)){
					return -1;
				}else if("mechanism".equals(type2) && "Retail investors".equals(type1)){
					return 1;
				}else{
					return 0;
				}
			})

)

4.2.2.5 flatMap

  • Flattening of flow, i.e. dimension reduction and merging. A list < list < string > > stream list passes through the list stream(). The processing of flatmap (item - > item. Stream()) becomes a list < string > stream.

//Question: stream < string [] > -- stream < string >?????
flatMap(strings -> Arrays.stream(strings))
flatMap(Arrays::stream )
//flatmap application scenario:
//Demand: output: Hello Zhangsan, hilisi
List<String> list1 = Arrays.asList("hello", "hi", "Hello");
List<String> list2 = Arrays.asList("zhangsan", "lisi", "wangwu");
list1.stream().flatMap(item1 ->list2.stream().map(item2 ->item1+":"+item2)).collect(Collectors.toList()).forEach(System.out::println);

4.2.3 termination

Common termination operations, such as list stream(). Filter (item - > item! = null) intermediate operation as an example

4.2.3.1 forEach

  • Internal iteration.

4.2.3.2 allMatch && anyMatch && noneMatch

  • Return Boolean type, allMatch - check whether all elements are matched, anyMatch - check whether any elements are matched, and noneMatch - check whether all elements are not matched. list. stream(). filter(item->item!=null). allMatch (item - > item > 0) returns true if all elements in the flow are greater than 0, otherwise false.

If the first element matches > 1000, the name of the first element is output If the second element does not conform to > 1000, it will not be executed further. If the first element conforms to > 1000, it will output the name of the first element and will not be executed further

  • Find out the names of students with missing records. The foreach in the Map set is in the form of (key,value)
//There are 20 students in the class, and each student has the examination results of 5 courses The score field of absent subjects is blank. You need to find out the names of students / / who are absent
//Map < string, list < examstudentscore > > studentmap: Map < student name, < student's English, mathematics and English scores > >
studentMap.foreach( (studentName,sutdentScoreList) ->{
	//anyMatch: whether any element conforms to the expression. If it returns true directly, it will not continue to execute
  	boolean bool = sutdentScoreList.stream.anyMatch(score -> {
  		return score.getScoreValue() == null
  	if(bool){
  		system.out.println(studentName);
  	}
  	});
});

4.2.3.3 findFirst and findAny search,

  • Returns the Optional < T > type. list. stream(). filter(item->item!=null). Findfirst() gets the Optional of T. If it is not null, use the get() method to get t; Or orElse(null).

 Stream<Integer> stream = Stream.of();
 Integer result = stream.findFirst().orElse(0);
 System.out.println(result);

4.2.3.4 max, min and count, count()

  • max() and min() receive a Comparator interface parameter and return the maximum and minimum options < T >. Stream.of(1, 2,3,4,5).max(Integer::compareTo) gets the maximum optional < integer > of the element, and then use get() to get 5. Supplement: when max has multiple records, take the first item of current

4.2.3.5 reduce reduction (convert elements in Stream stream into a value)

  • Graphically interpret the above three values

  • Receive a parameter: stream of(1,2,3,4,5). Reduce ((a, b) - > A + b), the result type is optional < integer >, call get() method to get 15.

  • Receive two parameters: stream of(1, 2, 3, 4, 5). Reduce (5, (a, b) - > A + b), the result type is Integer, the value is 20, and the first parameter is the initial value.

  • Receive three parameters: stream of(1, 2, 3, 4, 5). parallel(). Reduce (0, (a, b) - > A + B, (S1, S2) - > S1 + S2). The third parameter can only be executed in parallel streams. Its function is to finally merge the operation results of each parallel stream according to the operation rules of the third parameter.

  • Calculate the quantity of goods and the total amount of goods

list.stream().reduce(
	//Initial value
	new order(0,0,0.0),
	//Logic of two element calculation in stream
	(order1,order2)->{
		int count = order1.getCount+order2.getCount,
		int sum = order1.getSum+order2.getSum,
		return new order(0,count,sum)
	}
	//How to merge multiple parallel results in parallel
	(order1,order2)->{
		int count = order1.getCount+order2.getCount,
		int sum = order1.getSum+order2.getSum,
		return new order(0,count,sum)
	}
)

4.2.3.6 collect (convert elements in Stream stream into a container)

4.2.3.6.1 accept three parameters

//The most basic method
<R> R collect(//Result container
              Supplier<R> supplier,
              //Accumulate elements into the result container
              BiConsumer<R, ? super T> accumulator,
              //Merge result container
              BiConsumer<R, R> combiner);

//Map < user account, order (quantity and amount) >
public class StreamTest2 {
    public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
           add(new Order(1001,1));
           add(new Order(1002,2));
           add(new Order(1001,1));
           add(new Order(1003,3));
           add(new Order(1004,4));
           add(new Order(1005,5));
           add(new Order(1006,6));
        }};

        HashMap<Long,Order> result = list.stream().collect(
                //Initialize result container
                () -> new HashMap<Long,Order>(),
                //Add element to result container
                (HashMap<Long,Order> map, Order item) -> {
                    long account = item.getAccount();
                    if (map.containsKey(account)) {
                        Order order = map.get(account);
                        order.setCount(order.getCount()+item.getCount());
                    }else{
                        map.put(account,item);
                    }
                },
                //Parallel mode: parallel merging mode
                (HashMap<Long,Order> map1,HashMap<Long,Order> map2) ->{
                    map2.forEach( (key,value) -> {
                        map1.merge(key,value, (order1,order2) -> {
                            return new Order(key,order1.getCount() + order2.getCount());
                        });
                    });
                });
        System.out.println(result);
    }
}



public class StreamTest2 {
    public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
           add(new Order(1001,1));
           add(new Order(1002,2));
           add(new Order(1001,1));
           add(new Order(1003,3));
           add(new Order(1004,4));
           add(new Order(1005,5));
           add(new Order(1006,6));
        }};

        HashMap<Long,Order> result = list.stream().collect(
                //Initialize result container
                () -> {
                    System.out.println("Initialize result container");
                    return new HashMap<Long,Order>();},
                //Add element to result container
                (HashMap<Long,Order> map, Order item) -> {
                    System.out.println("Add element to result container");
                    long account = item.getAccount();
                    if (map.containsKey(account)) {
                        Order order = map.get(account);
                        order.setCount(order.getCount()+item.getCount());
                    }else{
                        map.put(account,item);
                    }
                },
                //If parallel streams are used to merge intermediate results
                (HashMap<Long,Order> map1,HashMap<Long,Order> map2) ->{
                    System.out.println("Merge intermediate results using parallel streams");
                    map2.forEach( (key,value) -> {
                    //If a key is found in map1, pass the value and value in map1 as a parameter to 											 order1,order2: processing logic can be customized
    				//If the key does not appear in map1, add the key and value to map1
    				//Finally, map1 is returned
                        map1.merge(key,value, (order1,order2) -> {
                            return new Order(key,order1.getCount() + order2.getCount());
                        });
                    });
                });
        result.forEach((account,order) -> {
            System.out.println("account"+account+",order:"+order);
        });
    }
}
//Print results
//Instead of using parallel flow, the result container will be initialized only once, and 7 elements will be added to the container continuously (results will be added 7 times)
/*
Initialize result container
 Add element to result container
 Add element to result container
 Add element to result container
 Add element to result container
 Add element to result container
 Add element to result container
 Add element to result container
account1001,order:Order{account=1001, count=2}
account1002,order:Order{account=1002, count=2}
account1003,order:Order{account=1003, count=3}
account1004,order:Order{account=1004, count=4}
account1005,order:Order{account=1005, count=5}
account1006,order:Order{account=1006, count=6}*/

//Adopt parallel flow
HashMap<Long,Order> result = list.stream().parallel().collect(......)
//Initialize the result container 7 times
//Add the elements to the result container 7 times
//Merge the intermediate results 6 times with parallel flow
   
/*
Initialize result container
 Initialize result container
 Add element to result container
 Add element to result container
 Initialize result container
 Add element to result container
 Initialize result container
 Add element to result container
 Initialize result container
 Initialize result container
 Add element to result container
 Initialize result container
 Add element to result container
 Merge intermediate results using parallel streams
 Merge intermediate results using parallel streams
 Add element to result container
 Merge intermediate results using parallel streams
 Merge intermediate results using parallel streams
 Merge intermediate results using parallel streams
 Merge intermediate results using parallel streams
*/

4.2.3.6.2. Accept Collector interface

  • Collect as List: List stream(). filter(item->item!=null). collect(Collectors.toList())

  • Collect as Set: list stream(). filter(item->item!=null). collect(Collectors.toSet())

  • Collect as HashSet: list stream(). filter(item->item!=null). collect(Collectors.toCollection(HashSet::new))

  • partitioningBy:

    Accept a predicate < T > return type: Map < Boolean, list < T > >; True the part that satisfies the condition; false: the part that does not meet the condition

    Accept one parameter & & accept two parameters as groupingBy

public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
            add(new Order(1001,22));
            add(new Order(1002,25));
            add(new Order(1003,26));
        }};
Map<Boolean,Map<Boolean,List<Order>>> result = list.stream().collect(
         Collectors.partitioningBy
         (item -> item.getAccount() == 1001,
         Collectors.partitioningBy( order -> order.getCount() > 21)));
    }
/** Printed results
  *false:{true=[Order{account=1002, count=22}, Order{account=1003, count=23},
  *        Order{account=1002, count=22}, Order{account=1003, count=23},                 *        Order{account=1002,count=25}, Order{account=1003, count=26}]}
  *true:{false=[Order{account=1001, count=21}, Order{account=1001, count=21}], true=     *        [Order{account=1001, count=22}]}
  */
  • Collect as Map

    Receive two parameters:

    public static void main(String[] args) {
          List<Order> list = new ArrayList<Order>(){{
                add(new Order(1001,1));
                add(new Order(1002,2));
                add(new Order(1003,3));
                add(new Order(1004,4));
                add(new Order(1005,5));
                add(new Order(1006,6));
            }};
    //The first parameter sets the key of map, and the second parameter sets the value of map
    //If Order::getAccount(key) has duplicate values, an exception will be thrown: Java lang.IllegalStateException:Duplicate key   
           Map<Long,Order> result1 = list.stream().collect(Collectors.toMap(Order::getAccount, Function.idenity()));
    }

    Three parameters are accepted:

public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
            add(new Order(1001,1));
            add(new Order(1001,1));
            add(new Order(1002,2));
            add(new Order(1003,3));
            add(new Order(1004,4));
            add(new Order(1005,5));
            add(new Order(1006,6));
        }};
Map<Long,Order> result2 = list.stream().collect(Collectors.toMap(
    	Order::getAccount,
        Function.identity(),
        //The first two parameters are the same as above
        //Processing logic when the same key appears
           (a, b) -> {
           a.setCount(a.getCount()+b.getCount());
           return a;}
       result2.forEach( (key,value) -> System.out.println(key+":"+value));
    }

Receive four parameters:

public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
            add(new Order(1001,1));
            add(new Order(1001,1));
            add(new Order(1002,2));
            add(new Order(1003,3));
            add(new Order(1004,4));
            add(new Order(1005,5));
            add(new Order(1006,6));
        }};
LinkedHashMap<Long,Order> result2 = list.stream().collect(Collectors.toMap(                  				   Order::getAccount,
                         Function.identity(),
                         (a, b) -> {
                            a.setCount(a.getCount()+b.getCount());
                            return a;},
                         //The first three parameters are the same as above, and the fourth parameter indicates to set the container receiving map as LinkedHashMap
                         LinkedHashMap::new));
       result2.forEach( (key,value) -> System.out.println(key+":"+value));
    }
  • Grouping by

Receive a parameter: group by key to get the type of map < key and the type of list < T > >.

@see #groupingByConcurrent(Function)
    
//Requirement: grouping by account (grouping by what is the key)
public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
            add(new Order(1001,21));
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1002,22));
            add(new Order(1003,23));
        }};
        Map<Long,List<Order>> result = list.stream().collect(Collectors.groupingBy(Order::getAccount));
        result.forEach((key,value) -> System.out.println(key+":"+value));
    }

Receive two parameters: the second parameter redefines the value collection type of map.

public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
          add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
            add(new Order(1001,22));
            add(new Order(1002,25));
            add(new Order(1003,26));
        }};
//Group according to the account (what is grouped according to is the key), and then group according to the quantity in the previous group
Map<Long,Map<Integer,List<Order>>> result = list.stream().collect(Collectors.groupingBy(Order::getAccount,Collectors.groupingBy(Order::getCount)));
        result.forEach((key,value) -> System.out.println(key+":"+value));
    }
/**Output results
 *1001:{21=[Order{account=1001, count=21}, Order{account=1001, count=21}], 22=       *		    [Order{account=1001, count=22}]}
 *1002:{22=[Order{account=1002, count=22}, Order{account=1002, count=22}], 25=       *          [Order{account=1002, count=25}]}
 *1003:{23=[Order{account=1003, count=23}, Order{account=1003, count=23}], 26=       *         [Order{account=1003, count=26}]}
  */

//Group according to the account (what is grouped according to is the key), and count the data in the group
Map<Long,Long> result = list.stream().collect(Collectors.groupingBy(Order::getAccount,Collectors.counting()));
        result.forEach((key,value) -> System.out.println(key+":"+value));

/** Output results
  *1001:3
  *1002:3
  *1003:3
  */


public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
            add(new Order(1001,22));
            add(new Order(1002,25));
            add(new Order(1003,26));
        }};
list.stream().collect(
          Collectors.
                groupingBy(
                     Order::getAccount,Collectors.collectingAndThen(
     //Return value of minby: optional < order > 
                         Collectors.minBy(Comparator.comparingInt(Order::getCount)),item -> item.get())))
   .forEach( (key,value) -> System.out.println(key+":"+value));
}

/** Printed results
  * 1001:Order{account=1001, count=21}
  * 1002:Order{account=1002, count=22}
  * 1003:Order{account=1003, count=23}
  */

Receive three parameters: the other two parameters are the same as above, and the third parameter is to provide a new result container

public static void main(String[] args) {
        List<Order> list = new ArrayList<Order>(){{
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
            add(new Order(1001,21));
            add(new Order(1002,22));
            add(new Order(1003,23));
            add(new Order(1001,22));
            add(new Order(1002,25));
            add(new Order(1003,26));
        }};
        
HashMap<Long,Long> result1 = list.stream().collect(
                //Provide a new result container: () - > New HashMap < > ()
                Collectors.groupingBy(Order::getAccount, () -> new HashMap<>(), Collectors.counting()));
        result1.forEach((key,value) -> System.out.println(key+":"+value));

    
HashMap<Long,Map<Integer,List<Order>>> result2 = list.stream().collect( 
                   //Provide a new result container: () - > New HashMap < > ()
Collectors.groupingBy(Order::getAccount,HashMap::new,Collectors.groupingBy(Order::getCount)));
result2.forEach((key,value) -> System.out.println(key+":"+value));
 System.out.println(result2.getClass());
}

Comprehensive case:

//Design an interface to provide external services, support the caller to pass in multiple account numbers to query orders, and group the account numbers to distinguish which order
//Which account does it belong to
Order:There are two properties:Account number,order number
public Map<String,list<Order>> query(list<String> accountIds){
    return Optional.ofNullable(selectFromDB(accountIds)).map(List::Stream)
    .orElseGet(Streram::empty)
    .collect(Collectors.groupingBy(order -> order.getAccountId))
}

Keywords: Java

Added by nz_mitch on Sat, 05 Feb 2022 12:47:34 +0200