java8 —— Stream

Posted by hmiller73 on Sat, 18 May 2019 16:44:03 +0200

Article directory

First, what is Stream?

It is a data channel for manipulating the sequence of elements generated by data sources (collections, arrays, etc.).

The ** stream ** in a collection is about data, and the Stream here is about computing!

Be careful:

  • Stream itself does not store elements.

  • Stream does not change the source object. Instead, they return to a new Stream that holds the results.

  • Stream operations are delayed. This means that they will wait until the results are needed.

2. Three steps of Stream operation

  • Create Stream
    A data source (such as a collection, an array) to obtain a stream
  • Intermediate operation
    An intermediate operation chain that processes data from data sources
  • Termination operation (terminal operation)
    A termination operation, an intermediate chain of operations, and results

3. Creating Stream

Collection provides two methods stream() and parallelStream()

List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream(); //Get a sequential stream
Stream<String> parallelStream = list.parallelStream(); //Getting a Parallel Flow

2. Get an array stream from stream() in Arrays

Integer[] nums = new Integer[10];
Stream<Integer> stream2 = Arrays.stream(nums);

3. Through the static method of() in Stream class

Using Stream.of(), create a stream by displaying values. It can receive any number of parameters.

Stream<Integer> stream3 = Stream.of(1,2,3,4,5,6);

4. Creating Infinite Flow

You can use the static methods Stream.iterate() and Stream.generate() to create an infinite flow.

//iteration
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
stream4.limit(10).forEach(System.out::println);

/ / generation

Stream<Double> stream5 = Stream.generate(Math::random).limit(2);
stream5.forEach(System.out::println);

IV. Stream's Intermediate Operation

Multiple intermediate operations can be connected to form a pipeline, unless the termination operation is triggered on the pipeline, the intermediate operation will not perform any processing! When terminating the operation, all the processes are disposed of at one time, which is called "inert evaluation".

4.1. Screening and slicing

  • filter: Receives Lambda and excludes certain elements from the stream.
  • limit: Cut off the stream so that its elements do not exceed a given number.
  • Skp (n): Skip the element and return a stream that throws away the first n elements. If there are less than n elements in the flow, an empty flow is returned. Complementarity with limit(n)
  • distinct: Screening to remove duplicate elements by hashCode() and equals() of the elements generated by the stream

Example:

public class TestStreamaAPI {	
	
	List<Employee> emps = Arrays.asList(
			new Employee(102, "Li Si", 59, 6666.66),
			new Employee(101, "Zhang San", 18, 9999.99),
			new Employee(103, "Wang Wu", 28, 3333.33),
			new Employee(104, "Zhao Liu", 8, 7777.77),
			new Employee(104, "Zhao Liu", 8, 7777.77),
			new Employee(104, "Zhao Liu", 8, 7777.77),
			new Employee(105, "Pseudo-ginseng", 38, 5555.55)
	);
	
	/*
	  Screening and slicing
		filter: Receive Lambda and exclude certain elements from the stream.
		limit: Cut off the flow so that its elements do not exceed a given number.
		skip(n) :  Skip the element and return to a stream that throws away the first n elements. If there are less than n elements in the flow, an empty flow is returned. Complementarity with limit(n)
		distinct :  Screening, removing duplicate elements by hashCode() and equals() of the elements generated by the stream
	 */
    
    //Internal Iteration: Iterative Operation Stream API Internal Completion
	@Test
	public void test2(){
		//Intermediate operation: no processing is done
		Stream<Employee> stream = emps.stream()
			.filter((e) -> {
				System.out.println("Test Intermediate Operation");
				return e.getAge() <= 35;
			});
		
		//Termination operation: Execute all content at once, called "inert evaluation"
		stream.forEach(System.out::println);
	}
	
	//External iteration: self-written iteration
	@Test
	public void test3(){
		Iterator<Employee> it = emps.iterator();
		
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
	
    //Only two "short circuits" were printed, not four.
	@Test
	public void test4(){
		emps.stream()
			.filter((e) -> {
				System.out.println("Short circuit!"); // &&  ||
				return e.getSalary() >= 5000;
			}).limit(2)
			.forEach(System.out::println);
	}
    
	//Only two "short circuits" were printed, not four. The difference from test4() is to skip the first two data and take the last two data.
	@Test
	public void test5(){
		emps.parallelStream()
			.filter((e) -> e.getSalary() >= 5000)
			.skip(2)
			.forEach(System.out::println);
	}
    
	//Employee's hashCode() and equals() must be rewritten, otherwise the de-duplication function will not take effect.
	@Test
	public void test6(){
		emps.stream()
			.distinct()
			.forEach(System.out::println);
	}
}

4.2. Mapping: (emphasis)

map: Receives Lambda, converts elements into other forms or extracts information. Receives a function as a parameter that is applied to each element and mapped to a new element.

flatMap: Receives a function as a parameter, replaces each value in the stream with another stream, and then connects all streams into one stream.

@Test
public void test1(){
    Stream<String> str = emps.stream()
        .map((e) -> e.getName());

    System.out.println("-------------------------------------------");

    List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");

    strList.stream()
        .map(String::toUpperCase)		
        .forEach(System.out::println);

    System.out.println("=====================================");

    //Extract names from Employee
    emps.stream()
        .map(Employee::getName)
        .forEach(System.out::println);

    System.out.println("=====================================");


    //The filterCharacter() method returns a stream for each character.
    Stream<Stream<Character>> stream2 = strList.stream()
        .map(TestStreamAPI1::filterCharacter);//  {{a,a,a},{b,b,b},{c,c,c}}

    stream2.forEach((sm) -> {
        sm.forEach(System.out::println); //{a,a,a,b,b,b,c,c,c}
    });

    System.out.println("=====================================");


    //
    Stream<Character> stream3 = strList.stream()
        .flatMap(TestStreamAPI1::filterCharacter);

    stream3.forEach(System.out::println);
}

public static Stream<Character> filterCharacter(String str){
    List<Character> list = new ArrayList<>();

    for (Character ch : str.toCharArray()) {
        list.add(ch);
    }
    return list.stream();
}

4.3. Sort:

  • sorted(): Comparable
  • sorted(Comparator com): Customized sorting
@Test
public void test2(){
    //Natural Sorting, by String's built-in Comparable Sorting
    emps.stream()
        .map(Employee::getName)
        .sorted()
        .forEach(System.out::println);

    System.out.println("------------------------------------");

    //Custom sort
    emps.stream()
        .sorted((x, y) -> {
            if(x.getAge() == y.getAge()){
                return x.getName().compareTo(y.getName());
            }else{
                return Integer.compare(x.getAge(), y.getAge());
            }
        }).forEach(System.out::println);
}

V. Termination of Stream

5.1. Finding and Matching

allMatch: Check if all elements match
anyMatch: Check whether at least one element matches
noneMatch: Check if there are no matching elements
findFirst: Returns the first element
findAny: Returns any element in the current stream
count: The total number of elements in the return stream
max: Maximum value in the return stream
min: Minimum value in the return stream

For Each: Internal Iteration

5.2. Reduction: (emphasis)

The elements in the flow are combined repeatedly to get a value.

Reduction (T identity, BinaryOperator) returns a result of T
Reduction (BinaryOperator) returns Optional < T >

	@Test
	public void test1(){
		List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
		
        //The starting value is 0, which is certainly not empty, so the return value is Integer.
		Integer sum = list.stream()
			.reduce(0, (x, y) -> x + y);
		
		System.out.println(sum);
		
		System.out.println("----------------------------------------");
		
        //Because there is no starting value, the return value is Optional
		Optional<Double> op = emps.stream()
			.map(Employee::getSalary)
			.reduce(Double::sum);
		
		System.out.println(op.get());
	}

5.3. Collection: (emphasis)

Convert the stream to other forms. Receive an implementation of the Collector interface for summarizing elements in Stream.

collect(Collector c)

The implementation of methods in the Collector interface determines how to perform collection operations (such as collecting List s, Set s, Maps) on the convection. But Collectors utility classes provide many static methods to easily create common collector instances

	List<Employee> emps = Arrays.asList(
			new Employee(102, "Li Si", 79, 6666.66, Status.BUSY),
			new Employee(101, "Zhang San", 18, 9999.99, Status.FREE),
			new Employee(103, "Wang Wu", 28, 3333.33, Status.VOCATION),
			new Employee(104, "Zhao Liu", 8, 7777.77, Status.BUSY),
			new Employee(104, "Zhao Liu", 8, 7777.77, Status.FREE),
			new Employee(104, "Zhao Liu", 8, 7777.77, Status.FREE),
			new Employee(105, "Pseudo-ginseng", 38, 5555.55, Status.BUSY)
	);

	@Test
	public void test4(){
		
		// Maximum value
		Optional<Double> max = emps.stream()
			.map(Employee::getSalary)
			.collect(Collectors.maxBy(Double::compare));
		
		System.out.println(max.get());
		
		//Minimum wage
		Optional<Employee> op = emps.stream()
			.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
		
		System.out.println(op.get());
		
		//The sum of wages
		Double sum = emps.stream()
			.collect(Collectors.summingDouble(Employee::getSalary));
		
		System.out.println(sum);
		
		//Average wage
		Double avg = emps.stream()
			.collect(Collectors.averagingDouble(Employee::getSalary));
		
		System.out.println(avg);
		
		// Total
		Long count = emps.stream()
			.collect(Collectors.counting());
		System.out.println(count);
		System.out.println("--------------------------------------------");
		
		
		DoubleSummaryStatistics dss = emps.stream()
			.collect(Collectors.summarizingDouble(Employee::getSalary));
		
		System.out.println(dss.getMax());
	}

5.4, grouping

Collectors.groupingBy();

	//Grouping
	@Test
	public void test5(){
		Map<Status, List<Employee>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus));
		
		System.out.println(map);
	}

Operation results:

{VOCATION=[Employee [id=103, name=Wang Wu, age=28, salary=3333.33, status=VOCATION]], BUSY=[Employee [id=102, name=Li Si, age=79, salary=6666.66, status=BUSY], Employee [id=104, name=Zhao Liu, age=8, salary=7777.77, status=BUSY], Employee [id=105, name=Pseudo-ginseng, age=38, salary=5555.55, status=BUSY]], FREE=[Employee [id=101, name=Zhang San, age=18, salary=9999.99, status=FREE], Employee [id=104, name=Zhao Liu, age=8, salary=7777.77, status=FREE], Employee [id=104, name=Zhao Liu, age=8, salary=7777.77, status=FREE]]}

Multilevel grouping:

	//Multilevel grouping
	@Test
	public void test6(){
		Map<Status, Map<String, List<Employee>>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
				if(e.getAge() >= 60)
					return "old age";
				else if(e.getAge() >= 35)
					return "middle age";
				else
					return "adult";
			})));
		
		System.out.println(map);
	}

Operation results:

{VOCATION={adult=[Employee [id=103, name=Wang Wu, age=28, salary=3333.33, status=VOCATION]]}, FREE={adult=[Employee [id=101, name=Zhang San, age=18, salary=9999.99, status=FREE], Employee [id=104, name=Zhao Liu, age=8, salary=7777.77, status=FREE], Employee [id=104, name=Zhao Liu, age=8, salary=7777.77, status=FREE]]}, BUSY={adult=[Employee [id=104, name=Zhao Liu, age=8, salary=7777.77, status=BUSY]], old age=[Employee [id=102, name=Li Si, age=79, salary=6666.66, status=BUSY]], middle age=[Employee [id=105, name=Pseudo-ginseng, age=38, salary=5555.55, status=BUSY]]}}

5.5, zoning

Collectors.partitioningBy()

	@Test
	public void test7(){
		Map<Boolean, List<Employee>> map = emps.stream()
			.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
		
		System.out.println(map);
	}

5.6, prefix, suffix

Collectors.joining()

	@Test
	public void test8(){
		String str = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.joining("," , "----", "----"));
		
		System.out.println(str);
	}

Topics: Lambda less