JDK1.8新特性总结 - 篇二(Stream API)

篇一中谈到了Lambda的基本语法,核心函数式接口以及方法引用的概念,现在让我们把关注点放到Stream Api上

一. Stream API

           流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合关注的是数据,而流关注的是计算!

           注意:

           1. Stream不会自己存储元素。

           2. Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。

           3. Stream操作是延迟的。

          Stream的操作大致分成以下三步:

       1. 创建Stream

              一个数据源(如:集合、数组), 获取一个流

/**
 * 创建Stream
 */
@org.junit.Test
public void test1(){
	//1. 通过Collection系列集合提供的stream()或parallelStream()将集合转换成集合流
	List<String> list = new ArrayList<>();
	Stream<String> stream1 = list.stream();

	//2. 通过Arrays的静态方法stream()将数组转换成数组流
	Employee[] employee = new Employee[10];
	Stream<Employee> stream2 = Arrays.stream(employee);

	//3. 通过Stream中的静态方法of()
	Stream<String> stream3 = Stream.of("a", "b", "c");

	//4. 创建无限流
	//4.1 迭代
	Stream<Integer> stream4 = Stream.iterate(0, x->x+2); //给出一个seed==0,根据一元表达式进行迭代产生无限流
	//stream4.forEach(System.out::println);

	//4.2 生成
	Stream<Double> stream5 = Stream.generate(Math::random);
	stream5.forEach(System.out::println);
}

        2. 中间操作

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

             筛选与切片:

             1. filter(Predicate predicate)       

                 接收Lambda,从流中排除某些元素。

             2. distinct()

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

             3. limit(long maxSize)

                 截断流,使其元素不超过给定的数量。

             4. skip(long n)

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

List<Employee> employees = Arrays.asList(
		new Employee("Jack1", 18, 8888),
		new Employee("Jack2", 20, 6666),
		new Employee("Jack3", 19, 9999),
		new Employee("Jack4", 25, 3333),
		new Employee("Jack4", 25, 3333),
		new Employee("Jack4", 25, 3333)
);

/**
 * 流水线式的中间操作 --- 筛选与切片
 */
@org.junit.Test
public void test(){
	//中间操作 - filter
	Stream<Employee> stream = employees.stream().filter(e -> {
		System.out.println("Stream Api的中间操作");
		return e.getAge() >= 20;
	}).distinct();

	//终止操作
	stream.forEach(System.out::println);

	//上述测试证实: 单纯的执行中间操作,不会有任何操作被执行。当且仅当执行了终止操作后,中间操作操作会被一次性的全部执行完毕。
}

/**
 * 流水线式的中间操作 --- 短路
 */
@org.junit.Test
public void test2(){
	employees.stream()
			.filter((x) -> x.getSalary() > 3000)
			.limit(2)
			.forEach(System.out::println);

	//中间操作会一次性加载所有的条件,如本例中,薪资大于4000的人有3位,但由于limit(2),因此当返回两位员工信息后,stream()就已经短路了。
	//正是有这种短路机制,在某种程度上说,提升了数据迭代的效率。
}

            映射:

            1. map(Function f) 

                 接收一个函数作为参数,该函数会被映射到每一个元素上,并将其映射成一个新的元素。

            2. mapToDouble(ToDoubleFunction f) 

                  接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的DoubleStream。

            3. mapToInt(ToIntFunction f)

                  接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的IntStream。

            4. mapToLong(ToLongFunction f)

                  接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的LongStream。

            5. flatMap(Function f);

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

/**
 * 映射
 */
@org.junit.Test
public void test3(){
	List<String> list1 = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");

	//实际上,这种操作就是在处理流中的每一个元素
	list1.stream()
			.map(String::toUpperCase)
			.forEach(System.out::println);

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

	//再举例: 从员工列表中提取出员工姓名列表
	employees.stream()
			.map(Employee::getName)
			.forEach(System.out::println);

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

	//测试flatMap,将list1中的每个字符串拆分成字符,组成新的集合
	list1.stream()
			.flatMap(this::getCharacters) //首先,把方法应用到每一个stream的元素身上,接着,从每一个元素返回的stream中,把子元素抽取出来,组合成一个新的集合。
			.forEach(System.out::print);  //输出: aaabbbcccdddeee

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

	//如果不使用flatMap(),新获得的集合是若干个stream组成的集合
	list1.stream()
			.map(this::getCharacters)
			.forEach(System.out::print); //输出 java.util.stream.ReferencePipeline$Head@71f2a7d5java.util.stream.ReferencePipeline$Head@2cfb4a64j
}

            排序:

             1. sorted() 

                 产生一个新流,其中按自然顺序排序。

             2. sorted()

                 产生一个新流,其中按比较器的顺序来排序。

/**
 * 排序
 * sorted() - 自然排序(Comparable)
 * sorted(Comparator com) - 定制排序(Comparator)
 */
@org.junit.Test
public void test4(){
	List<String> list1 = Arrays.asList("ccc", "eee", "aaa", "ddd", "bbb");

	//按照自然排序
	list1.stream()
			.sorted()
			.forEach(System.out::print); //输出: aaabbbcccdddeee
	System.out.println();

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

	//按照定制排序

	//先按照薪水降序排序,如果薪水相同,再按照姓名降序升序
	employees.stream()
			.sorted((e1, e2)-> {
				if(e1.getSalary() == e2.getSalary()){
					return e1.getName().compareTo(e2.getName());
				}else{
					return -e1.getSalary().compareTo(e2.getSalary());
				}
			})
			.forEach(System.out::println);
}

       3. 终止操作(终端操作)

              一个终止操作,执行中间操作链并产生结果。

             1. 查找与匹配

                 1. allMatch 检查是否匹配所有元素

                 2. anyMatch 检查是否至少匹配一个元素

                 3. noneMatch 检查是否没有匹配所有元素

                 4. findFirst 返回第一个元素

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

                 6. count 返回流中元素的总个数

                 7. max  返回流中的最大值

                 8. min 返回流中的最小值

List<Employee> employees = Arrays.asList(
		new Employee("Jack1", 18, 8888D, Status.BUSY),
		new Employee("Jack2", 20, 6666D, Status.RELAX),
		new Employee("Jack3", 19, 9999D, Status.VOCATION),
		new Employee("Jack4", 25, 3333D, Status.RELAX),
		new Employee("Jack4", 25, 3333D, Status.VOCATION),
		new Employee("Jack4", 25, 3333D, Status.BUSY),
		new Employee("Jack5", 25, 7777D, Status.BUSY),
		new Employee("Jack6", 25, 7777D, Status.VOCATION)
);

@org.junit.Test
public void test1(){
	boolean b1 = employees.stream()
			.allMatch(e -> e.getStatus().equals(Status.BUSY));
	System.out.println("所有员工是否都处于BUSY状态: " + b1); //false

	boolean b2 = employees.stream()
			.anyMatch(e -> e.getStatus().equals(Status.BUSY));
	System.out.println("是否有员工的状态为BUSY: " + b2);  //true

	boolean b3 = employees.stream()
			.noneMatch(e -> e.getStatus().equals(Status.EXCITING));
	System.out.println("是否没有任何一位员工处于EXCITING状态:" + b3); // true

	Optional<Employee> op1 = employees.stream()
			.sorted((e1, e2) -> {
				return -e1.getSalary().compareTo(e2.getSalary());
			})
			.findFirst();
	System.out.println("薪酬待遇最高的员工信息: " + op1.get());

	Optional<Employee> op2 = employees.stream()
			.sorted((e1, e2) -> {
				return -e1.getSalary().compareTo(e2.getSalary());
			})
			.findAny();
	System.out.println("任意一名员工的信息: " + op2.get());

	Long sum = employees.stream().count();
	System.out.println("员工数量: " + sum);

	Optional<Employee> op3 = employees.stream()
			.max((e1, e2) -> e1.getSalary().compareTo(e2.getSalary()));
	System.out.println("薪酬待遇最高的员工信息: " + op3.get());

	Optional<Double> op4 = employees.stream()
			.map(Employee::getSalary)
			.min(Double::compare);
	System.out.println("最低工资: " + op4.get());
}

              2. 归约

                 reduce(T identity, BinaryOperator op)  和 reduce(BinaryOperator)  将流中的元素反复的结合起来,得到一个值。

                一般与map结合使用,被称作map-reduce模式。先用map进行数据加工(或提取),再通过reduce对 数据进行反复运算。

@org.junit.Test
public void test(){
	List<Integer> lists = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

	//求出集合中所有数字的和
	Integer sum = lists.stream()
			.reduce(0, (Integer::sum)); //identity是一个起始值
	 System.out.println(sum);

	//之所以这里得到的结果时Optional<Integer>,是因为此次归约中没有给定初始值,
	//这样就有可能导致最终的结果为null,因此jdk1.8使用容器类Optional来装载结果
	Optional<Integer> reduce = lists.stream()
			.reduce(Integer::sum);

}  

               3. 收集  collect

public void test2(){
	List<String> stringList = employees.stream()
			.map(Employee::getName)
			.collect(Collectors.toList());
	stringList.forEach(System.out::println);

	//将收集的结果放入Set中
	Set<String> collect = employees.stream()
			.map(Employee::getName)
			.collect(Collectors.toSet());

	//将收集的结果放入任意的Collection集合中
	HashSet<String> collect1 = employees.stream()
			.map(Employee::getName)
			.collect(Collectors.toCollection(HashSet::new));  //Supplier接口,自己实现即可

	//总数
	Long sum = employees.stream()
			.collect(Collectors.counting());

	//平均值
	Double avgSalary = employees.stream()
			.collect(Collectors.averagingDouble(Employee::getSalary));

	//总和
	Double sumSalary = employees.stream()
			.collect(Collectors.summingDouble(Employee::getSalary));

	//最大值
	Optional<Employee> op1 = employees.stream()
			.collect(Collectors.maxBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary())));
}

              4. 分组

/**
 * 分组
 */
@org.junit.Test
public void test3(){
	//以状态为员工进行分组
	Map<Status, List<Employee>> collect = employees.stream()
			.collect(Collectors.groupingBy(Employee::getStatus));

	System.out.println(collect);

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

	//多级分组
	//先按状态分组,再按年龄段分组
	//注意: collect(Function, Collect) 可以无限嵌套
	Map<Status, Map<String, List<Employee>>> map = employees.stream()
			.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
				if(((Employee)(e)).getAge() <= 30){
					return "青年";
				}else if(((Employee)(e)).getAge() <= 50){
					return "中年";
				}else{
					return "老年";
				}
			})));
	System.out.println(map);
}

               5. 分区(分片)

/**
 *  分区
 *  满足条件和不满足条件,分成“false”和“true”两个区返回
 */
@org.junit.Test
public void test4(){
	//按照薪资待遇是否大于7000进行分区
	Map<Boolean, List<Employee>> map = employees.stream()
			.collect(Collectors.partitioningBy((e) -> e.getSalary() > 7000));
	System.out.println(map);
}

              6. 总结(概要)

/**
 * 总结(概要)
 */
@org.junit.Test
public void test5(){
	DoubleSummaryStatistics dss = employees.stream()
			.collect(Collectors.summarizingDouble(Employee::getSalary));

	System.out.println(dss.getMax());
	System.out.println(dss.getAverage());
	System.out.println(dss.getCount());
	System.out.println(dss.getMin());
	System.out.println(dss.getSum());
}

             7. 连接

@org.junit.Test
public void test6(){
	String joinedStr1 = employees.stream()
			.map(Employee::getName)
			.collect(Collectors.joining(","));
	System.out.println(joinedStr1); // Jack1,Jack2,Jack3,Jack4,Jack4,Jack4,Jack5,Jack6

	//如果想在首部或尾部接上特定字符串,可以按照以下写法
	String joinedStr2 = employees.stream()
			.map(Employee::getName)
			.collect(Collectors.joining(",","我爱你","祖国")); 
	System.out.println(joinedStr2); //我爱你Jack1,Jack2,Jack3,Jack4,Jack4,Jack4,Jack5,Jack6祖国
}
发布了45 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/miaomiao19971215/article/details/90518126