Java8新特性——StreamAPI 的使用

StreamAPI的说明

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

为什么要用StreamAPI

实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要java层面去处理。

Stream到底是是什么

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,Stream讲的是计算!”

注意:
Stream 自己不会存储元素。
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream操作的三个步骤

1- 创建 Stream
一个数据源(如:集合、数组),获取一个流
2- 中间操作
一个中间操作链,对数据源的数据进行处理
3- 终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
如图所示:

创建Stream

有三种方式:
1、通过集合(Java8中的Collection接口被扩展没提供了两个获取流的方法 Stream() parallelStream())
2、通过数组(Java8 中的 Arrays 的静态方法 stream() 可以获取数组流 static Stream stream(T[] array): 返回一个流)
3、通过Stream的of()方法(可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数 public static Stream of(T... values) : 返回一个流)
首先有个Person类

package com.xnn.lambda;
/**
 * 类(接口)描述:
 * @author xnn
 * 2018年10月21日下午3:05:37
 */
public class Person {
private String name;
private int age;
private double salary;
private Status status;
/**
 * @return the name
 */
public String getName() {
	return name;
}
/**
 * @param name the name to set
 */
public void setName(String name) {
	this.name = name;
}
/**
 * @return the age
 */
public int getAge() {
	return age;
}
/**
 * @param age the age to set
 */
public void setAge(int age) {
	this.age = age;
}
/**
 * @return the salary
 */
public double getSalary() {
	return salary;
}

public void setSalary(double salary) {
	this.salary = salary;
}


/**
 * @param name
 * @param age
 * @param salary
 * @param status
 */
public Person(String name, int age, double salary, Status status) {
	super();
	this.name = name;
	this.age = age;
	this.salary = salary;
	this.status = status;
}
public Person() {
	super();
}
/**
 * @return the status
 */
public Status getStatus() {
	return status;
}
/**
 * @param status the status to set
 */
public void setStatus(Status status) {
	this.status = status;
}

/**
 * @param name
 * @param age
 * @param salary
 */
public Person(String name, int age, double salary) {
	super();
	this.name = name;
	this.age = age;
	this.salary = salary;
}
/* (non-Javadoc)
 * @see java.lang.Object#hashCode()
 */
@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + age;
	result = prime * result + ((name == null) ? 0 : name.hashCode());
	long temp;
	temp = Double.doubleToLongBits(salary);
	result = prime * result + (int) (temp ^ (temp >>> 32));
	result = prime * result + ((status == null) ? 0 : status.hashCode());
	return result;
}
/* (non-Javadoc)
 * @see java.lang.Object#equals(java.lang.Object)
 */
@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	Person other = (Person) obj;
	if (age != other.age)
		return false;
	if (name == null) {
		if (other.name != null)
			return false;
	} else if (!name.equals(other.name))
		return false;
	if (Double.doubleToLongBits(salary) != Double
			.doubleToLongBits(other.salary))
		return false;
	if (status != other.status)
		return false;
	return true;
}

/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
@Override
public String toString() {
	return "Person [name=" + name + ", age=" + age + ", salary=" + salary
			+ ", status=" + status + "]";
}

public enum Status {
	FREE,
	BUSY,
	VACATION;
}
}

package com.xnn.stream;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;

/**
 * 类(接口)描述:Stream 操作的三个步骤
 * 1.创建流
 * 2.做一些中间操作
 * 3.做终止操作
 * @author xnn
 * 2018年10月22日下午2:56:26
 */
public class TestStreamAPI {
//获取流的第一种方式   (Java8中的Collection接口被扩展没提供了两个获取流的方法 Stream()  parallelStream())
	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66),
			new Person( "张三", 18, 9999.99),
			new Person( "王五", 28, 3333.33),
			new Person( "赵六", 8, 7777.77),
			new Person( "田七", 38, 5555.55)
	);
	@Test
	public void test1() {
		//获取一个顺序流
		Stream<Person> stream = person.stream();
		System.out.println(stream);
		
		//获取一个并行流
		Stream<Person> parallelStream = person.parallelStream();
		System.out.println(parallelStream);
        
		//获取流的第二种方式 ——由数组创建流
		Integer arr[] = new Integer[] {1,4,5,6,78,7};
		Stream<Integer> stream2 = Arrays.stream(arr);
		Optional<Integer> first = stream2.findFirst();
		System.out.println(first);
		//获取流的第三种方式——可以使用静态方法 Stream.of(),即用值创建流
		Stream<String> stream3 = Stream.of("y7yiu");	
	}
	@Test
	public void test2() {
		//创建无限流
				//迭代的方式创建无限流
				Stream.iterate(0,(x)->x+2).limit(10)
				.forEach(System.out::println);;
				//生成的方式产生无限流
				Stream.generate(()->Math.random()).limit(3).forEach(System.out::println);;
	}
}

运行结果:

java.util.stream.ReferencePipeline$Head@5ef04b5
java.util.stream.ReferencePipeline$Head@5f4da5c3
Optional[1]
0
2
4
6
8
10
12
14
16
18
0.20849306218015928
0.526378190370348
0.6520277965773994

Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

中间操作——筛选与切片


代码示例

 * 
 */
package com.xnn.stream;

import java.net.StandardSocketOptions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;
import com.xnn.lambda.Person.Status;

/**
 * 类(接口)描述://中间操作
 * 
 * @author xnn 2018年10月22日下午3:27:12
 */
public class TestStreamAPI2 {

	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66, Status.FREE),
			new Person("张三", 18, 9999.99, Status.FREE),
			new Person("王五", 28, 3333.33, Status.BUSY),
			new Person("赵六", 8, 7777.77, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.BUSY),
			new Person("田七", 38, 5555.55, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.VACATION)

	);
	/*
	 * 筛选与切片 filter——接收 Lambda , 从流中排除某些元素。 limit——截断流,使其元素不超过给定数量。 
	 * skip(n) ——跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。
	 * 与 limit(n) 互补
	 * distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
	 */
	@Test
	public void test1() {
		// 先获取流
		Stream<Person> stream = person.stream();

		// 筛选 Stream<T> filter(Predicate<? super T> predicate);
		// Predicate接口的test方法 传递进去一个T 返回一个Boolean
		// filter——接收 Lambda , 从流中排除某些元素
		// 这个forEach被称为是内部迭代
		stream.filter((u) -> {
			System.out.println("中间操作");
			//筛选出年龄大于20 的人员
			return u.getAge() > 20;
		}).forEach(System.out::println);
		System.out.println("==============================");
		/**
		 * 下面这一部分由于没有终止操作,所以"中间操作"这四个字并没有打印出来,只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
		 */
		Stream<Person> stream4 = person.stream();
		stream4.filter((u) -> {
			System.out.println("中间操作");
			return u.getAge() > 20;
		});
		System.out.println("==============================");

		// 必须再创建一个新流 目测是新功能的时候,不能用上面的流,否则会报流已经打开或关闭的错误
		Stream<Person> stream1 = person.stream();

		// limit——截断流,使其元素不超过给定数量
		//筛选出年龄大于20岁的人元信息后,只取前两个值
		stream1.filter((u) -> u.getAge() > 20).limit(2)
				.forEach(System.out::println);
		System.out.println("==============================");

		// skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
		Stream<Person> stream2 = person.stream();
		//筛选出年龄大于20岁的人元信息后,跳过第一个人,取后面的值
		stream2.filter((u) -> u.getAge() > 20).skip(1)
				.forEach(System.out::println);

		System.out.println("==============================");
		
		// distinct——筛选,通过流所生成元素的 hashCode() 和 equals()这样去除重复元素(意即需要重写实体类的hashcode 和equals方法)
		Stream<Person> stream3 = person.stream();

		stream3.filter((u) -> u.getAge() > 20).distinct()
				.forEach(System.out::println);
	}
}

运行结果:

中间操作——映射


代码示例

package com.xnn.stream;

import java.net.StandardSocketOptions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;
import com.xnn.lambda.Person.Status;

/**
 * 类(接口)描述://中间操作
 * 
 * @author xnn 2018年10月22日下午3:27:12
 */
public class TestStreamAPI2 {

	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66, Status.FREE),
			new Person("张三", 18, 9999.99, Status.FREE),
			new Person("王五", 28, 3333.33, Status.BUSY),
			new Person("赵六", 8, 7777.77, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.BUSY),
			new Person("田七", 38, 5555.55, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.VACATION)

	);
/**
	 * 映射: 1、map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
	 * 2、mapToDouble(ToDoubleFunction f)) 接收一个函数作为参数,该函数会被应用到每个素上,产生一个新的
	 * DoubleStream 3、mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的
	 * IntStream。 4、mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的
	 * LongStream 5、flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
	 * 
	 * @author:xnn 2018年10月22日下午3:55:54
	 */
	@Test
	public void test2() {
		Stream<Person> stream = person.stream();
		// 1、map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
		// 把姓名的集合取出来
		// stream.map((x)->x.getName()).forEach(System.out::println);
		//方法引用的方式写的
		stream.map(Person::getName).forEach(System.out::println);
		System.out.println("================");

		// 2、mapToDouble(ToDoubleFunction f)) 接收一个函数作为参数,该函数会被应用到每个素上,产生一个DoubleStream
		Stream<Person> stream1 = person.stream();
		// stream1.mapToDouble((x)->x.getSalary())
		// .forEach(System.out::println);
		// 另一种写法 即:接受了Person类的getSalary()方法为参数,把薪资全都取出胡来了
		stream1.mapToDouble(Person::getSalary).forEach(System.out::println);
		System.out.println("================");

		// 3、mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
		Stream<Person> stream2 = person.stream();
		stream2.mapToInt(Person::getAge).forEach(System.out::println);
		System.out.println("================");

		Stream<String> stream3 = Stream.of("aaa", "bbb", "ccc", "ddd");
		Stream<Stream<Character>> map =
	    /*TestStreamAPI2::filterCharacter就代表把每一个字符串("aaa","bbb"...)都进行filterCharacter()处理,
		这时候每个字母,都变成了一个流,而map()方法返回的也是一个流,所以返回值就成了Stream<Stream<Character>>*/
		 stream3.map(TestStreamAPI2::filterCharacter);
		
		 //用map的话还得多重遍历(sm 代表一个Stream<Character>,所以再对sm进行了遍历)
		 map.forEach((sm)->sm.forEach(System.out::println));
                System.out.println("++++++++++++++++++++++++++");
        
        // flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,
        //然后把所有流连接成一个流(是个关键)
		Stream<String> stream4 = Stream.of("aaa", "bbb", "ccc", "ddd");
		Stream<Character> flatMap = stream4
				.flatMap(TestStreamAPI2::filterCharacter);
		flatMap.forEach(System.out::println);
		System.out.println("==============");
		
		/**
		 * map 和flatmap类似于list集合的add()方法和addAll()方法
		 */
		List<Object> list =new ArrayList<>();
		List<Object> list2 =new ArrayList<>();
		list.add("aaa");
		list.add("bbb");
		list2.add("1");
		//add是把集合加进去了
		list2.add(list);
		System.out.println(list2);
		
		System.out.println("+++++++++++++++++++++++++");
		List<Object> list3 =new ArrayList<>();
		List<Object> list4 =new ArrayList<>();
		list3.add("aaa");
		list3.add("bbb");
		list4.add("1");
		//addAll是把集合中的每一个元素加进去了
		list4.addAll(list3);
		System.out.println(list4);
		
	}

	// 把字符串转化为字符数组流
	public static Stream<Character> filterCharacter(String str) {
		List<Character> list = new ArrayList<>();
		for (Character character : str.toCharArray()) {
			list.add(character);
		}
		return list.stream();
	}
}

运行结果

李四
张三
王五
赵六
田七
田七
田七
================
6666.66
9999.99
3333.33
7777.77
5555.55
5555.55
5555.55
================
59
18
28
8
38
38
38
================
a
a
a
b
b
b
c
c
c
d
d
d
++++++++++++++++++++++++++
a
a
a
b
b
b
c
c
c
d
d
d
==============
用add的结果:[1, [aaa, bbb]]
+++++++++++++++++++++++++
用addAll的结果:[1, aaa, bbb]

中间操作——排序


代码示例

package com.xnn.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;
import com.xnn.lambda.Person.Status;

/**
 * 类(接口)描述://中间操作
 * 
 * @author xnn 2018年10月22日下午3:27:12
 */
public class TestStreamAPI2 {

	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66, Status.FREE),
			new Person("张三", 18, 9999.99, Status.FREE),
			new Person("王五", 28, 3333.33, Status.BUSY),
			new Person("赵六", 8, 7777.77, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.BUSY),
			new Person("田七", 38, 5555.55, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.VACATION)

	);
/**
	 * sorted() 产生一个新流,其中按自然顺序 sorted(Comparator comp) 产生一个新流,其中按比较器顺序
	 */
	@Test
	public void test3() {
		// sorted() 产生一个新流,其中按自然顺序
		List<String> list = Arrays.asList("aa", "bb", "cc", "dd", "ee");
		Stream<String> stream = list.stream();
		stream.sorted().forEach(System.out::println);
		System.out.println("-------------------------");

		// sorted(Comparator comp) 产生一个新流,其中按比较器顺序
		person.stream().sorted((e1, e2) -> {
			/**
			 * 年龄相同按姓名排序,否则按年龄排序
			 */
			if (e1.getAge() == e2.getAge()) {
				return e1.getName().compareTo(e2.getName());
			} else {
				return Integer.compare(e1.getAge(), e2.getAge());
			}
		}).forEach(System.out::println);;
	}
}

运行结果

aa
bb
cc
dd
ee
-------------------------
Person [name=赵六, age=8, salary=7777.77, status=VACATION]
Person [name=张三, age=18, salary=9999.99, status=FREE]
Person [name=王五, age=28, salary=3333.33, status=BUSY]
Person [name=田七, age=38, salary=5555.55, status=BUSY]
Person [name=田七, age=38, salary=5555.55, status=VACATION]
Person [name=田七, age=38, salary=5555.55, status=VACATION]
Person [name=李四, age=59, salary=6666.66, status=FREE]

Stream 的终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
流进行了终止操作后,不能再次使用。

终止操作——匹配与查找


代码示例

package com.xnn.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;
import com.xnn.lambda.Person.Status;

/**
 * 类(接口)描述://中间操作
 * 
 * @author xnn 2018年10月22日下午3:27:12
 */
public class TestStreamAPI2 {

	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66, Status.FREE),
			new Person("张三", 18, 9999.99, Status.FREE),
			new Person("王五", 28, 3333.33, Status.BUSY),
			new Person("赵六", 8, 7777.77, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.BUSY),
			new Person("田七", 38, 5555.55, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.VACATION)

	);
/**
	 * stream的终止操作(查找与匹配) 
	 * 1、allMatch(Predicate p) 检查是否匹配所有元素
	 * 2、anyMatch(Predicate p) 检查是否至少匹配一个元素 
	 * 3、noneMatch(Predicate p)检查是否没有匹配所有元素 
	 * 4、findFirst() 返回第一个元素 
	 * 5、findAny() 返回当前流中的任意元素
	 * @author:xnn 2018年10月23日下午2:48:07
	 */
	@Test
	public void test4() {
		// allMatch(Predicate p) 检查是否匹配所有元素,即所有元素的status是BUSY才返回true
		boolean b = person.stream()
				.allMatch((e) -> e.getStatus().equals(Status.BUSY));
		System.out.println(b);
		System.out.println("===================");

		// anyMatch(Predicate p) 检查是否至少匹配一个元素,只要有一个元素的status是BUSY就返回true
		boolean c = person.stream()
				.anyMatch((w) -> w.getStatus().equals(Status.BUSY));
		System.out.println(c);
		System.out.println("===================");

		// noneMatch(Predicate p) 检查是否没有匹配所有元素,即 所有的元素的status都不是BUSY才返回true
		boolean d = person.stream()
				.noneMatch((q) -> q.getStatus().equals(Status.BUSY));
		System.out.println(d);
		System.out.println("===================");

		// findFirst() 返回第一个元素,返回符合条件的第一个元素
		Optional<Person> findFirst = person.stream().sorted(
				(e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
				.findFirst();
		System.out.println(findFirst.get());
		System.out.println("========================");

		// findAny() 返回当前流中的任意元素()
		Optional<Person> any = person.parallelStream()
				.filter((e) -> e.getStatus().equals(Status.BUSY)).findAny();
		System.out.println(any);
	}
}

运行结果

false
===================
true
===================
false
===================
Person [name=王五, age=28, salary=3333.33, status=BUSY]
========================
Optional[Person [name=田七, age=38, salary=5555.55, status=BUSY]]

终止操作——匹配与查找二


代码示例:


package com.xnn.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;
import com.xnn.lambda.Person.Status;

/**
 * 类(接口)描述://中间操作
 * 
 * @author xnn 2018年10月22日下午3:27:12
 */
public class TestStreamAPI2 {

	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66, Status.FREE),
			new Person("张三", 18, 9999.99, Status.FREE),
			new Person("王五", 28, 3333.33, Status.BUSY),
			new Person("赵六", 8, 7777.77, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.BUSY),
			new Person("田七", 38, 5555.55, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.VACATION)

	);
/**
	 * 1、count() 返回流中元素总数 
	 * 2、max(Comparator c) 返回流中最大值
	 * 3、min(Comparator c)返回流中最小值 
	 * 4、forEach(Consumer c) 内部迭代(使用 Collection接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做
	 */
	@Test
	public void test5() {
		// count() 返回流中元素总数
		long count = person.stream().count();
		System.out.println(count);
		System.out.println("=======华丽的分割线========");

		// max(Comparator c) 返回流中最大值
		Optional<Person> max = person.stream().max(
				(e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
		System.out.println("最高工资员工为" + max);
		System.out.println("=======华丽的分割线========");

		// min(Comparator c) 返回流中最小值
		Optional<Person> min = person.stream().min(
				(e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
		System.out.println("最低工资员工为" + min);
	}
}

运行结果

7
=======华丽的分割线========
最高工资员工为Optional[Person [name=张三, age=18, salary=9999.99, status=FREE]]
=======华丽的分割线========
最低工资员工为Optional[Person [name=王五, age=28, salary=3333.33, status=BUSY]]

终止操作——归约


map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
代码示例:


package com.xnn.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;
import com.xnn.lambda.Person.Status;

/**
 * 类(接口)描述://中间操作
 * 
 * @author xnn 2018年10月22日下午3:27:12
 */
public class TestStreamAPI2 {

	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66, Status.FREE),
			new Person("张三", 18, 9999.99, Status.FREE),
			new Person("王五", 28, 3333.33, Status.BUSY),
			new Person("赵六", 8, 7777.77, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.BUSY),
			new Person("田七", 38, 5555.55, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.VACATION)

	);
/**
	 * 归约 
	 * 1、reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回 T
	 * 2、reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
	 */
	@Test
	public void test6() {
		// reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回 T
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		/**
		 * 先把0作为了x,y是流中的第一个元素 相加得1,然后1作为x,流中的第二个元素作为y,就这样,依次加下去。
		 */
		Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
		System.out.println(reduce);
		System.out.println("=======华丽的分割线========");

		// reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
		/**
		 * 可以看出两个reduce方法返回的并不一样,因为第一个reduce方法有初始值,最后所得的值肯定不会为空,而第二种情况则有可能会为空,所以返回值为跑optional
		 */
		Optional<Double> reduce2 = person.stream().map(Person::getSalary)
				.reduce(Double::sum);
		System.out.println(reduce2);
		System.out.println("=======华丽的分割线========");

	}
}

运行结果:

55
=======华丽的分割线========
Optional[44444.40000000001]
=======华丽的分割线========

终止操作——收集


Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:


代码示例

package com.xnn.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.xnn.lambda.Person;
import com.xnn.lambda.Person.Status;

/**
 * 类(接口)描述://中间操作
 * 
 * @author xnn 2018年10月22日下午3:27:12
 */
public class TestStreamAPI2 {

	List<Person> person = Arrays.asList(
			new Person("李四", 59, 6666.66, Status.FREE),
			new Person("张三", 18, 9999.99, Status.FREE),
			new Person("王五", 28, 3333.33, Status.BUSY),
			new Person("赵六", 8, 7777.77, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.BUSY),
			new Person("田七", 38, 5555.55, Status.VACATION),
			new Person("田七", 38, 5555.55, Status.VACATION)

	);
/**
	 * 收集: collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
	 */
	@Test
	public void test7() {
		//把人员的名字放到一个list中去
		List<String> collect = person.stream().map(Person::getName)
				.collect(Collectors.toList());
		System.out.println(collect);
          System.out.println("=======华丽的分割线=======");
		
          //把人员的名字放到一个hashset中去(会自动去重)
		HashSet<String> collect2 = person.stream().map(Person::getName)
				.collect(Collectors.toCollection(HashSet::new));
		System.out.println(collect2);
        System.out.println("=======华丽的分割线=======");

		//计数
		Long long1 = person.stream().collect(Collectors.counting());
		System.out.println(long1);
        System.out.println("=======华丽的分割线=======");

		// 按状态分组
		Map<Status, List<Person>> collect3 = person.stream()
				.collect(Collectors.groupingBy((e) -> e.getStatus()));
		System.out.println(collect3);
        System.out.println("=======华丽的分割线=======");

		// 多重分组(先按状态分组,再按薪资是否大于5000分组)
		Map<Status, Map<Boolean, List<Person>>> collect4 = person.stream()
				.collect(Collectors.groupingBy((e) -> e.getStatus(),
						Collectors.groupingBy((e) -> e.getSalary() > 5000)));
		System.out.println(collect4);
        System.out.println("=======华丽的分割线=======");
        
        //取所有人员薪资的平均值
		Double double1 = person.stream()
				.collect(Collectors.averagingDouble((e) -> e.getSalary()));
		System.out.println("所有人员薪资的平均值:"+double1);
        System.out.println("=======华丽的分割线=======");
         
        //把所有人的姓名拼接成一个字符串
		String collect5 = person.stream().map(Person::getName)
				.collect(Collectors.joining());
		System.out.println(collect5);
        System.out.println("=======华丽的分割线=======");
         
        //找出所有人中薪资最少的一个员工的信息。
		Optional<Person> optional = person.stream().collect(Collectors.minBy(
				(e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
		System.out.println(optional);
        System.out.println("=======华丽的分割线=======");

	}
}

运行结果

[李四, 张三, 王五, 赵六, 田七, 田七, 田七]
=======华丽的分割线=======
[李四, 张三, 王五, 赵六, 田七]
=======华丽的分割线=======
7
=======华丽的分割线=======
{BUSY=[Person [name=王五, age=28, salary=3333.33, status=BUSY], Person [name=田七, age=38, salary=5555.55, status=BUSY]], FREE=[Person [name=李四, age=59, salary=6666.66, status=FREE], Person [name=张三, age=18, salary=9999.99, status=FREE]], VACATION=[Person [name=赵六, age=8, salary=7777.77, status=VACATION], Person [name=田七, age=38, salary=5555.55, status=VACATION], Person [name=田七, age=38, salary=5555.55, status=VACATION]]}
=======华丽的分割线=======
{BUSY={false=[Person [name=王五, age=28, salary=3333.33, status=BUSY]], true=[Person [name=田七, age=38, salary=5555.55, status=BUSY]]}, FREE={true=[Person [name=李四, age=59, salary=6666.66, status=FREE], Person [name=张三, age=18, salary=9999.99, status=FREE]]}, VACATION={true=[Person [name=赵六, age=8, salary=7777.77, status=VACATION], Person [name=田七, age=38, salary=5555.55, status=VACATION], Person [name=田七, age=38, salary=5555.55, status=VACATION]]}}
=======华丽的分割线=======
所有人员薪资的平均值:6349.2
=======华丽的分割线=======
李四张三王五赵六田七田七田七
=======华丽的分割线=======
Optional[Person [name=王五, age=28, salary=3333.33, status=BUSY]]
=======华丽的分割线=======
原文地址:https://www.cnblogs.com/nnxud/p/9844593.html