JDK8新特性之Stream快速入门

目录

什么是Stream

Stream 是 Java8 中处理集合的类。

不熟悉Lambda表达式的可以参考JDK8新特性之Lambda表达式快速入门

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

Stream 操作的三个步骤

  1. 创建 Stream
    一个数据源(如:集合、数组),获取一个流。
  2. 中间操作
    一个或多个中间操作链,对数据源的数据进行处理。
  3. 终止操作
    一个终止操作,执行中间操作链,并产生结果。
    在这里插入图片描述

一、创建 Stream

并行流与串行流

Java8 中的 Collection 接口被扩展,提供了两个创建流的方法:

public interface Collection<E> extends Iterable<E> {
	//...

	default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

	default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }

	//...
}

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换。

示例:

Long sum = LongStream.rangeClosed(0L, 10000000000L)
							 .parallel()
							 .sum();

四种创建Stream流的方式

1.Collection 提供了两个方法 stream() 与 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
2.通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(nums);
3.通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
4. 创建无限流
//迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);

//生成
Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);

二、中间操作

示例演示的基础数据:

// new Employee(int id, String name, int age, 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)
	);

筛选与切片

filter:筛选 ,从流中筛选出符合的元素。

示例:

@Test
public void test1(){
	//所有的中间操作不会做任何的处理
	Stream<Employee> stream = emps.stream()
		.filter((e) -> {
			System.out.println("filter中间操作,我是:" + e.getName());
			return e.getAge() <= 35;
		});
	System.out.println("---开始---");
	//只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
	stream.forEach(System.out::println);
}

示例输出:

---开始---
filter中间操作,我是:李四
filter中间操作,我是:张三
Employee [id=101, name=张三, age=18, salary=9999.99]
filter中间操作,我是:王五
Employee [id=103, name=王五, age=28, salary=3333.33]
filter中间操作,我是:赵六
Employee [id=104, name=赵六, age=8, salary=7777.77]
filter中间操作,我是:赵六
Employee [id=105, name=赵六, age=8, salary=7777.77]
filter中间操作,我是:赵六
Employee [id=106, name=赵六, age=8, salary=7777.77]
filter中间操作,我是:田七

通俗的讲:filter一下,符合条件,forEach一下。

limit(n):截断流,使其元素不超过n个。

示例:

@Test
public void test2(){
	emps.stream()
		.filter((e) -> {
			System.out.println("filter中间操作,我是:" + e.getName());
			return e.getAge() <= 35;
		}).limit(3) //不是先筛选出所有符合的再从中拿3个,而是只符合3次就不在继续了
		.forEach(System.out::println);
}

示例输出:

filter中间操作,我是:李四
filter中间操作,我是:张三
Employee [id=101, name=张三, age=18, salary=9999.99]
filter中间操作,我是:王五
Employee [id=103, name=王五, age=28, salary=3333.33]
filter中间操作,我是:赵六
Employee [id=104, name=赵六, age=8, salary=7777.77]

通俗的讲:filter一下,符合条件,forEach一下。当符合3次后流就终止了。

skip(n) :跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。

示例:

@Test
public void test3(){
	emps.parallelStream()
		.filter((e) -> {
			System.out.println("filter中间操作,我是:" + e.getName());
			return e.getAge() <= 35;
		})
		.skip(2)//跟limit()不通,skip()是先筛选出所有符合的再跳过前2个
		.forEach(System.out::println);
}

示例输出:

filter中间操作,我是:赵六
filter中间操作,我是:李四
filter中间操作,我是:赵六
filter中间操作,我是:赵六
filter中间操作,我是:王五
filter中间操作,我是:张三
filter中间操作,我是:田七
Employee [id=104, name=赵六, age=8, salary=7777.77]
Employee [id=105, name=赵六, age=8, salary=7777.77]
Employee [id=106, name=赵六, age=8, salary=7777.77]

通俗的讲:filter完所有,符合条件的跳过2个,再一起forEach。

distinct:筛选,通过元素的 hashCode() 和 equals() 去除重复元素

示例:

@Test
public void test(){
	emps.stream()
		.distinct()
		.forEach(System.out::println);
}

映射

map():把元素映射成一个新元素

示例:

@Test
public void test1(){
	Stream<String> empStream = emps.stream().map((e) -> e.getName());
	empStream.forEach(System.out::println);
	
	List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
	Stream<String> stream = strList.stream().map(String::toUpperCase);
	stream.forEach(System.out::println);
}
flatMap():接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
public class TestStream {
	public static Stream<Character> filterCharacter(String str){
		List<Character> list = new ArrayList<>();
		for (Character ch : str.toCharArray()) {
			list.add(ch);
		}
		return list.stream();
	}
	
	@Test
	public void test2(){
		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
		
		Stream<Stream<Character>> stream2 = strList.stream()
				   .map(TestStream::filterCharacter);
		stream2.forEach((sm) -> {
			sm.forEach(System.out::println);
		});
			
		Stream<Character> stream3 = strList.stream()
			   .flatMap(TestStream::filterCharacter);
		stream3.forEach(System.out::println);
	}
}

排序

sorted():自然排序
@Test
public void test1(){
	emps.stream()
		.map(Employee::getName)
		.sorted()
		.forEach(System.out::println);
}
sorted(Comparator comp):自定义排序
@Test
public void test2(){
	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);
}

三、终止操作

查找与匹配

基础数据:

List<Employee> emps = Arrays.asList(
		//new Employee(int id, String name, int age, double salary, Status status)
		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)
);
allMatch:检查是否匹配所有元素
anyMatch:检查是否至少匹配一个元素
noneMatch:检查是否没有匹配的元素
@Test
public void test1(){
    //检查是否所有员工都是忙碌状态
	boolean b1 = emps.stream()
		.allMatch((e) -> e.getStatus().equals(Status.BUSY));
	System.out.println("allMatch: "+b1); //false

	//检查是否有员工是忙碌状态
	boolean b2 = emps.stream()
		.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
	System.out.println("anyMatch: "+b2); //true

	//检查是否没有员工是忙碌状态
	boolean b3 = emps.stream()
		.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
	System.out.println("noneMatch: "+b3); //false
}
findFirst:返回第一个元素
findAny:返回当前流中的任意元素
@Test
public void test2(){
	Optional<Employee> op = emps.stream()
		.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
		.findFirst();
	System.out.println(op.get());
	
	Optional<Employee> op2 = emps.parallelStream()
		.filter((e) -> e.getStatus().equals(Status.FREE))
		.findAny();
	
	System.out.println(op2.get());
}
count:返回流中元素的总个数
max:返回流中最大值
min:返回流中最小值
@Test
public void test3(){
	long count = emps.stream()
					 .filter((e) -> e.getStatus().equals(Status.FREE))
					 .count();
	System.out.println(count);
	
	Optional<Double> op = emps.stream()
		.map(Employee::getSalary)
		.max(Double::compare);
	System.out.println(op.get());
	
	Optional<Employee> op2 = emps.stream()
		//.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
		.min(Comparator.comparingDouble(Employee::getSalary));
	System.out.println(op2.get());
}
forEach():循环迭代
@Test
public void test4(){
	emps.stream().forEach(System.out::println);
}

规约

reduce() 可以将流中元素反复结合起来,得到一个值。

有两种用法:reduce(T identity, BinaryOperator) 、 reduce(BinaryOperator)。
示例1:

@Test
public void test1(){
	List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
	
	//累加list集合里的值
	Integer sum = list.stream()
		.reduce(0, (x, y) -> x + y);
	System.out.println(sum);
	
	//累加所有员工的工资
	Optional<Double> op = emps.stream()
		.map(Employee::getSalary)
		.reduce(Double::sum);
	System.out.println(op.get());
}

示例2:
需求:搜索员工名字中 “六” 出现的次数。

@Test
public void test2(){
	Optional<Integer> sum = emps.stream()
		.map(Employee::getName)
		.flatMap((str) -> {
			List<Character> list = new ArrayList<>();
			for (Character ch : str.toCharArray()) {
				list.add(ch);
			}
			return list.stream();
		})
		.map((ch) -> {
			if(ch.equals("六")) {
				return 1;
			}else {
				return 0;
			}
		}).reduce(Integer::sum);
	
	System.out.println(sum.get());
}

收集

collect():把流收集起来转换为其他形式

语法:collect(Collector c),将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。
JDK8中提供 Collectors 类提有很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下。

Collectors.toList()把流中元素收集到List。
List<String> list = emps.stream().map(Employee::getName).collect(Collectors.toList());
Collectors.toSet()把流中元素收集到Set。
Set<String> set = emps.stream().map(Employee::getName).collect(Collectors.toSet());
Collectors.toCollection()把流中元素收集到创建的集合,比如:HashSet、TreeSet。
HashSet<String> hashSet= emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
TreeSet<Integer> treeSet= emps.stream().map(Employee::getId).collect(Collectors.toCollection(TreeSet::new));
Collectors.counting()计算流中元素的个数。
Long count = emps.stream().collect(Collectors.counting());
Collectors.summingDouble()对流中元素求和。
Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));
Collectors.averagingDouble()对流中元素求平均值。
Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));
Collectors.summarizingDouble()对流中元素求统计值。
DoubleSummaryStatistics statistics = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
double maxValue = statistics.getMax();
double minValue = statistics.getMin();
double sumValue = statistics.getSum();
double avgValue = statistics.getAverage();
long countValue = statistics.getCount();
Collectors.maxBy()根据比较器选择最大值。
Optional<Double> max = emps.stream().map(Employee::getSalary).collect(Collectors.maxBy(Double::compare));
double maxValue = max.get();
Collectors.minBy()根据比较器选择最小值。
Optional<Employee> op = emps.stream().collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
Employee minEmp = op.get();
Collectors.reducing()规约就累加值。
Optional<Double> sum = emps.stream().map(Employee::getSalary).collect(Collectors.reducing(Double::sum));
Double sumValue1 = sum.get();
Double sumValue2 = emps.stream().map(Employee::getSalary).collect(Collectors.reducing(0D, Double::sum));
Collectors.collectingAndThen()转换函数返回的类型。
int listSize= emps.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
Collectors.groupingBy()根据某一个属性分组。
Map<Status, List<Employee>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus));
Collectors.groupingBy()多级分组。
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 "成年";
	})));
Collectors.partitioningBy()分区,根据true或false进行分区。
Map<Boolean, List<Employee>> map = emps.stream().collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));

早期写的另一篇博客里也有很多用法示例 JDK8语法糖stream()流式计算的使用例子

此篇文件结束。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值