带你走进stream世界

带你走进stream世界

        • 引言
        • 什么是 Stream?
        • 怎么生成流
        • 一些基本方法
            • forEach
            • map
            • filter
            • limit
            • sorted
            • 并行(parallel)程序
            • Collectors
            • 统计
        • 基本方法的灵活运用
            • 中间操作
            • 终止操作
            • 创建Employee运功pojo类
            • 取出所有员工姓名
            • 按照员工年龄排序,年龄一样按姓名排
            • 检查员工是否包含某一状态
            • 找到第一个工资最低的员工
            • 找到任意一个status是FREE的员工
            • 找到status是FREE的总个数
            • 找到员工工资最高的工资
            • 找到员工工资最低的员工信息
            • 注意:流进行了终止操作后,不能再次使用
          • 终止操作 归约
            • 累加员工工资
            • 将员工姓名全部取出来放到一个指定集合
            • 使用归约统计员工的最高、最低、平均、总和工资
            • 按照员工状态进行分组
            • 按照员工状态和年龄进行多级分组
            • 按照员工工资进行分区
        • Optional 容器类:用于尽量避免空指针异常
        • 附:Lambda 表达式的基础语法

引言

学习stream之前看一下Lambda表达式语法学习更快喔!
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

怎么生成流

在 Java 8 中, 集合接口有两个方法来生成流:
stream() − 为集合创建串行流
parallelStream() − 为集合创建并行流

一些基本方法

forEach

Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

		//写法一
		Random random = new Random();
		//ints 就是intStream  x自己取的名字 代表你传入的值
		random.ints().limit(10).forEach((x)->{
			System.out.println(x);
		});
		//写法二
		random.ints().limit(10).forEach(System.out::println);
			
map

map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

		List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
		//Collectors.toList() 收集成你想要的类型集合
		numbers.stream().map(i->i*i).collect(Collectors.toList()).forEach(System.out::println);
filter

ilter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

		List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
		// 获取空字符串的数量
		strings.stream().filter(str -> str.isEmpty()).count();
limit

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

		Random random = new Random();
		random.ints().limit(10).forEach(System.out::println);
sorted

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

		Random random = new Random();
		random.ints().limit(10).sorted().forEach(System.out::println);
并行(parallel)程序

parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:

		List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
		// 获取空字符串的数量
		strings.parallelStream().filter(string -> string.isEmpty()).count();
Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

		List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
		List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
		 
		System.out.println("不为空的字符串: " + filtered);
		String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
		System.out.println("合并字符串: " + mergedString);
统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

		List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
		//summaryStatistics 为一个收集器
		IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
		 
		System.out.println("列表中最大的数 : " + stats.getMax());
		System.out.println("列表中最小的数 : " + stats.getMin());
		System.out.println("所有数之和 : " + stats.getSum());
		System.out.println("平均数 : " + stats.getAverage());

基本方法的灵活运用

中间操作

带你走进stream世界_第1张图片

终止操作

以下forEach allMatch anyMatch noneMatch findFirst findAny count max min这些方法均为流的终止操作

创建Employee运功pojo类
public class Employee {

	//此处省略getter setter hashCode toString
	//编号
	private int id;
	//姓名
	private String name;
	//年龄
	private int age;
	//工资
	private double salary;
}
取出所有员工姓名
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66),
			new Employee(101, "张三", 18, 9999.99),
			new Employee(103, "王五", 28, 3333.33),
			new Employee(104, "赵六", 8, 7777.77),
			new Employee(104, "赵六", 8, 7777.77),
			new Employee(104, "赵六", 8, 7777.77),
			new Employee(105, "田七", 38, 5555.55)
	);
		emps.stream()
			.map(Employee::getName)
			.forEach(System.out::println);
按照员工年龄排序,年龄一样按姓名排
		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);

compareTo() 方法用于两种方式的比较:
字符串与对象进行比较。
按字典顺序比较两个字符串。

检查员工是否包含某一状态
    //新建一个enum类,可以直接写到Employee这个类的内部
	public enum Status {
		FREE, BUSY, VOCATION;
	}
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66, Status.BUSY),
			new Employee(101, "张三", 18, 9999.99, Status.FREE),
			new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
			new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
			new Employee(104, "赵六", 8, 7777.77, Status.FREE),
			new Employee(104, "赵六", 8, 7777.77, Status.FREE),
			new Employee(105, "田七", 38, 5555.55, Status.BUSY)
	);
			boolean bl = emps.stream()
				.allMatch((e) -> e.getStatus().equals(Status.BUSY));	
			System.out.println(bl);		
			boolean bl1 = emps.stream()
				.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
			
			System.out.println(bl1);	
			boolean bl2 = emps.stream()
				.noneMatch((e) -> e.getStatus().equals(Status.BUSY));		
			System.out.println(bl2);

allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素

找到第一个工资最低的员工
		Optional<Employee> op = emps.stream()
			.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
			.findFirst();
		System.out.println(op.get());

findFirst——返回第一个元素
Optional 是java8中一个可以有效避免空指针异常的方法

找到任意一个status是FREE的员工
		Optional<Employee> op2 = emps.parallelStream()
			.filter((e) -> e.getStatus().equals(Status.FREE))
			.findAny();
		System.out.println(op2.get());

findAny——返回当前流中的任意元素

找到status是FREE的总个数
		long count = emps.stream()
						 .filter((e) -> e.getStatus().equals(Status.FREE))
						 .count();

count——返回流中元素的总个数

找到员工工资最高的工资
		Optional<Double> op = emps.stream()
			.map(Employee::getSalary)
			.max(Double::compare);

max——返回流中最大值

找到员工工资最低的员工信息
		Optional<Employee> op2 = emps.stream()
			.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));

min——返回流中最小值

注意:流进行了终止操作后,不能再次使用
		Stream<Employee> stream = emps.stream()
		 .filter((e) -> e.getStatus().equals(Status.FREE));
		//count已经为终止操作了
		long count = stream.count();
		//错误使用
		stream.map(Employee::getSalary)
			.max(Double::compare);
终止操作 归约

归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。

累加员工工资
		List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
		Integer sum = list.stream()
			.reduce(0, (x, y) -> x + y);
		System.out.println(sum);
		System.out.println("----------------------------------------");
		Optional<Double> op = emps.stream()
			.map(Employee::getSalary)
			.reduce(Double::sum);
		System.out.println(op.get());
将员工姓名全部取出来放到一个指定集合

collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

		List<String> list = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.toList());
		
		list.forEach(System.out::println);
		System.out.println("----------------------------------");
		Set<String> set = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.toSet());
		set.forEach(System.out::println);
		System.out.println("----------------------------------");
		HashSet<String> hs = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.toCollection(HashSet::new));
		hs.forEach(System.out::println);
使用归约统计员工的最高、最低、平均、总和工资
		Optional<Double> max = emps.stream()
			.map(Employee::getSalary)
			.collect(Collectors.maxBy(Double::compare));
		
		System.out.println(max.get());
		
		Optional<Employee> op = emps.stream()
			.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
		
		System.out.println(op.get());
		
		Double sum = emps.stream()
			.collect(Collectors.summingDouble(Employee::getSalary));
		
		System.out.println(sum);
				Optional<Double> sum2 = emps.stream()
				.map(Employee::getSalary)
				.collect(Collectors.reducing(Double::sum));
			
			System.out.println(sum2.get());
		
		Double avg = emps.stream()
			.collect(Collectors.averagingDouble(Employee::getSalary));
		
		System.out.println(avg);
		
		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());
按照员工状态进行分组
		Map<Status, List<Employee>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus));
		System.out.println(map);
按照员工状态和年龄进行多级分组
		Map<Status, Map<String, List<Employee>>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
				if(e.getAge() >= 60)
					return "老年";
				else if(e.getAge() >= 35)
					return "中年";
				else
					return "成年";
			})));
		
		System.out.println(map);
按照员工工资进行分区
		Map<Boolean, List<Employee>> map = emps.stream()
			.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
		
		System.out.println(map);

Optional 容器类:用于尽量避免空指针异常

		Optional<Employee> op = Optional.of(new Employee());
		Employee emp = op.get();
		System.out.println(emp);

Optional.of(T t) : 创建一个 Optional 实例

		Optional<Employee> op = Optional.ofNullable(new Employee());
		
		if(op.isPresent()){
			System.out.println(op.get());
		}
		
		Employee emp = op.orElse(new Employee("张三"));
		System.out.println(emp);
		
		Employee emp2 = op.orElseGet(() -> new Employee());
		System.out.println(emp2);

Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值

		Optional<Employee> op = Optional.of(new Employee(101, "张三", 18, 9999.99));
		
		Optional<String> op2 = op.map(Employee::getName);
		System.out.println(op2.get());
		
		Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
		System.out.println(op3.get());

map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional

附:Lambda 表达式的基础语法

一、Lambda 表达式的基础语法:Java8中引入了一个新的操作符 “->” 该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分:
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
语法格式一:无参数,无返回值
() -> System.out.println(“Hello Lambda!”);
语法格式二:有一个参数,并且无返回值
(x) -> System.out.println(x)
语法格式三:若只有一个参数,小括号可以省略不写
x -> System.out.println(x)
语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
Comparator com = (x, y) -> {
System.out.println(“函数式接口”);
return Integer.compare(x, y);
};
语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
Comparator com = (x, y) -> Integer.compare(x, y);
语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
(Integer x, Integer y) -> Integer.compare(x, y);
二、Lambda 表达式需要“函数式接口”的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰
可以检查是否是函数式接口

下期再见。。。

你可能感兴趣的:(stream,lambda,java)