JDK1.8新特性学习笔记-Stream

 

流Stream其实就是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列,集合是数据,流就是计算,把数据经过一系列流水线式的中间操作以后产生一个新流 。1)stream不会自己存储元素 ,2)stream不会改变源对象 3)stream操作是延迟的执行的,这意味着他们会等到需要结果时才执行(后面代码演示)。

Stream操作的三个步骤:

1.创建Stream:创建数据源(集合,数组等),获取一个流

2.中间操作:一个中间操作链,对数据源的数据进行处理

3.终止操作:一个终止操作,执行中间操作链,产生结果

	    //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. 创建无限流
		//迭代 一元运算+2无限进行,可以用limit限制次数
		Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
		stream3.forEach(System.out::println);
		
		//生成
		Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
		stream4.forEach(System.out::println);

中间操作:所有中间操作不会做任何处理

Stream<Employee> stream = emps.stream()
			.filter((e) -> {
				System.out.println("测试中间操作");
				return e.getAge() <= 35;
			});

此时运行程序不会打印,只有当做终止操作时,所有中间操作会一次性的全部执行

stream.forEach(System.out::println);

1.筛选和切片类型的中间操作
filter:接受Lambda,从流中排除某些元素(eg:如上面的例子)
limit:截断流使其元素不超过给定数值

	@Test
	public void test4(){
		emps.stream()
			.filter((e) -> {
				System.out.println("短路!"); // &&  ||
				return e.getSalary() >= 5000;
			}).limit(3)
			.forEach(System.out::println);
	}

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

@Test
	public void test5(){
		emps.parallelStream()
			.filter((e) -> e.getSalary() >= 5000)
			.skip(2)
			.forEach(System.out::println);
	}

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

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

2.映射类型

map:接受Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

    @Test
	public void test1(){
		
		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
		
		Stream<String> stream = strList.stream()
			   .map(String::toUpperCase);
		
		stream.forEach(System.out::println);
    }

flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流.

	@Test
	public void test1(){
		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
		Stream<Character> stream3 = strList.stream()
			   .flatMap(TestStreamAPI1::filterCharacter);
		
		stream3.forEach(System.out::println);
	}

3.排序类型

sort:自然排序

    @Test
	public void test2(){
		emps.stream()
			.map(Employee::getName)
			.sorted()
			.forEach(System.out::println);
    }

sorted:定制排序:

    @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);
	}

终止操作:

1.查找与匹配类型:

allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值

@Test
	public void test1(){
			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);
	}
	
	@Test
	public void test2(){
		Optional<Employee> op = emps.stream()
			.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
			.findFirst();
		
		System.out.println(op.get());
		
		System.out.println("--------------------------------");
		
		Optional<Employee> op2 = emps.parallelStream()
			.filter((e) -> e.getStatus().equals(Status.FREE))
			.findAny();
		
		System.out.println(op2.get());
	}
	
	@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()));
		
		System.out.println(op2.get());
	}
	
	//注意:流进行了终止操作后,不能再次使用
	@Test
	public void test4(){
		Stream<Employee> stream = emps.stream()
		 .filter((e) -> e.getStatus().equals(Status.FREE));
		
		long count = stream.count();
		
		stream.map(Employee::getSalary)
			.max(Double::compare);
	}

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

	@Test
	public void test1(){
		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());
	}

map和reduce的连接通常称为map-reduce,因谷歌用他搜索而出名,hadoop中会学到。先map提取,再用reduce归约

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

函数

	@Test
	public void test4(){
		//方法一
		//员工总数
		Long count = emps.stream().collect(Collectors.counting());
		System.out.println(count);
		//工资总和
		Double sum = emps.stream()
				.collect(Collectors.summingDouble(Employee::getSalary));
		System.out.println(sum);
		//工资平均值
		Double avg = emps.stream()
				.collect(Collectors.averagingDouble(Employee::getSalary));
		System.out.println(avg);
		//工资最大值员工的工资
		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());
		
		//方法二:
		DoubleSummaryStatistics dss = emps.stream()
				.collect(Collectors.summarizingDouble(Employee::getSalary));
		System.out.println(dss.getMax());
		System.out.println(dss.getMin());
		System.out.println(dss.getCount());
		System.out.println(dss.getSum());
	}

分组

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

多级分组

	@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 "老年";
				else if(e.getAge() >= 35)
					return "中年";
				else
					return "成年";
			})));
		
		System.out.println(map);
	}

分区

@Test
	public void test7(){
		//true和false两个区,满足条件一个区,不满足的一个区
		Map<Boolean, List<Employee>> map = emps.stream()
			.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
		
		System.out.println(map);
	}

连接

	@Test
	public void test8(){
		//字符串连接,把名字连接,可根据参数中间拿,间隔。首尾用---
		String str = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.joining("," , "----", "----"));
		
		System.out.println(str);
	}

并行流

并行流,就是把一个内容分成多块,并用不同的线程处理不同的内容块,java8做了优化,底层是fork/join。把大任务拆分(forlk)后,分别处理,处理完(join)把结果汇总,充分利用了多核。

fork/join与传统线程池的区别,工作窃取模式。。。简单的说就是自己的内容快干完了,会随机去其他线程上从后面窃取一个任务去处理,减少了线程等待,提高了效率。

 1.7中fork/join用法

public class ForkJoinCalculate extends RecursiveTask<Long>{

	/**
	 * 
	 */
	private static final long serialVersionUID = 13475679780L;
	
	private long start;
	private long end;
	
	private static final long THRESHOLD = 10000L; //临界值
	
	public ForkJoinCalculate(long start, long end) {
		this.start = start;
		this.end = end;
	}
	
	@Override
	protected Long compute() {
		long length = end - start;
		
		if(length <= THRESHOLD){
			long sum = 0;
			
			for (long i = start; i <= end; i++) {
				sum += i;
			}
			
			return sum;
		}else{
			long middle = (start + end) / 2;
			
			ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
			left.fork(); //拆分,并将该子任务压入线程队列
			
			ForkJoinCalculate right = new ForkJoinCalculate(middle+1, end);
			right.fork();
			
			return left.join() + right.join();
		}
		
	}

进行累加操作一下为测试类

	@Test
	public void test1(){
		long start = System.currentTimeMillis();
		
		ForkJoinPool pool = new ForkJoinPool();
		ForkJoinTask<Long> task = new ForkJoinCalculate(0L, 10000000000L);
		
		long sum = pool.invoke(task);
		System.out.println(sum);
		
		long end = System.currentTimeMillis();
		
		System.out.println("耗费的时间为: " + (end - start)); //112-1953-1988-2654-2647-20663-113808
	}

为了对比,写了一个单线程for循环的累加操作

@Test
	public void test2(){
		long start = System.currentTimeMillis();
		
		long sum = 0L;
		
		for (long i = 0L; i <= 10000000000L; i++) {
			sum += i;
		}
		
		System.out.println(sum);
		
		long end = System.currentTimeMillis();
		
		System.out.println("耗费的时间为: " + (end - start)); //34-3174-3132-4227-4223-31583
	}

注释为对比数据结果,当累加输小的时候 第1次测试数据时1E,单线程较快,因为处理数据较少,fork/join的拆分反而浪费了资源,当累加到100E时,单线程明显变慢。

下面是1.8优化后写法

	@Test
	public void test3(){
		long start = System.currentTimeMillis();
		
		Long sum = LongStream.rangeClosed(0L, 10000000000L)
							 .parallel()
							 .sum();
		
		System.out.println(sum);
		
		long end = System.currentTimeMillis();
		
		System.out.println("耗费的时间为: " + (end - start)); //2061-2053-2086-18926
	}

代码简洁且效率比1.7中的fork/join效率还要高。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值