Java--8--新特性--Stream API
Stream API 提供了一种高效且易于使用的处理数据的方式,(java.util.stream.*)
他可以对数组,集合等做一些操作,最终产生一个新的流,原数据是不会发生改变的。
“集合”讲的是数据,“流”讲的是计算!
注意:
1. Stream自己不会储存元素
2. 不会改变源对象,相反,它会产生一个新的Stream
3. 操作是延迟执行的,这意味着他们会等到需要结果的时候才执行
package StreamP; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Stream1 { /** * Stream 操作三步骤 * 1. 创建Stream * 2. 中间操作 * 3. 终止操作 */ //创建Stream public void test(){ //可以通过Collection系列集合的 stream()方法 或 parallelStream()创建流 List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //可以通过Arrays中的静态方法 stream() 得到一个数组流 Integer[] integers = new Integer[2]; Stream<Integer> stream1 = Arrays.stream(integers); //通过Stream中的of()方法 Stream<String> stringStream = Stream.of("a", "b"); //创建无限流 //迭代 第一个参数其实就是一个起始值,第二个参数是一个接口,代表一元运算 Stream<Integer> stream2 = Stream.iterate(0,(x)->x+2); //生成 需要的是一个供给型接口 Stream<Double> generate = Stream.generate(() -> Math.random()); generate.limit(5).forEach(System.out::println); // 0.9108574640882595 // 0.8772485678436105 // 0.00318519741701917 // 0.3682731233198696 // 0.39645909717553074 } }
下面是Stream 的中间操作的一些方法
筛选与切片测试代码
package StreamP; import LambdaP.Employee; import org.junit.Test; import java.util.Arrays; import java.util.List; public class Stream2 { //中间操作:多个中间操作可以连接起来形成一个流水线,除非触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称为惰性求值 /** * 筛选与切片 * filter-接收Lambda,从流中排除某些元素 * limit-截断流,使元素不超过给定数量 * slip(n)-跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补 * distinct-筛选,通过流所生成元素的hashCode(),equals()去除重复元素 */ public static List<Employee> employees = Arrays.asList( new Employee("张三", 19, 2000), new Employee("张四", 33, 3000), new Employee("张五", 38, 4000), new Employee("张六", 41, 2500), new Employee("张七", 42, 5500), new Employee("张七", 42, 5500), new Employee("张七", 42, 5500) ); public void test(){ employees.stream().filter((x) -> x.getage()==19).forEach(System.out::println); //Employee{name='张三', age=19, salary=2000} 意思就是遍历集合取出每一个对象,如果对象中的Age等于19,那么他就会通过filter } public void test1(){ employees.stream().filter((x) ->x.getage()<42).limit(2).forEach(System.out::println); //Employee{name='张三', age=19, salary=2000} 意思就是取出age<42的前两个对象 limit跟数据库的limit基本一样 //Employee{name='张四', age=33, salary=3000} 注意这里的limit,如果找到指定数量的数据后,循环迭代就自己结束了,不会一直遍历 } public void test2(){ employees.stream().filter((x)->x.getage()<42).skip(2).forEach(System.out::println); //LambdaP.Employee{name='张五', age=38, salary=4000} //LambdaP.Employee{name='张六', age=41, salary=2500} //意思就是跳过前两个,输出满足条件的其他对象数据 } @Test public void test3(){ //到这一步才新添加的重复的“张七”,以上的操作时没有重复元素的 employees.stream().distinct().forEach(System.out::println); //去重操作!注意!!!!!必须重写去重目标对象中的hashCode与equals方法! } }
映射的操作代码
package StreamP; import LambdaP.Employee; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Stream3 { /** * 中间操作‘ * 映射 * map-接收Lambda,将元素转换成其他形式或提取信息。接受一个函数作为参数,该函数会被应用到每个元素上,并将 其映射成一个新的元素。 * flatMap-接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 */ public static List<Employee> employees = Arrays.asList( new Employee("张三", 19, 2000), new Employee("张四", 33, 3000), new Employee("张五", 38, 4000), new Employee("张六", 41, 2500), new Employee("张七", 42, 5500), new Employee("张七", 42, 5500), new Employee("张七", 42, 5500) ); public void test(){ //所有变大写 List<String> list = Arrays.asList("a","b","c","d","e"); list.stream() .map((x) -> x.toUpperCase())//需要一个Function接口 .forEach(System.out::println); System.out.println("**************************************"); //取出所有的Name employees.stream().map(Employee::getName).distinct().forEach(System.out::println); /** * map他会将流中的每一个元素都应用到map后的表达式上! */ } //需求:把每个字母都转换成char public void test1(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee"); Stream<Character> aChar = getChar(list); aChar.forEach(System.out::println); } public static Stream<Character> getChar(List<String> list){ List<Character> lists = new ArrayList<>(); //遍历传入的list,现在的lambda中的e为"aaa","bbb","ccc","ddd","eee" list.stream().forEach((e)->{ char[] ch = e.toCharArray(); for (char c : ch) { lists.add(c); } }); return lists.stream(); } //需求:把每个字母都转换成char public void test2(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee"); //这里的返回是一个流套流,因为map本身会返回一个流,并且调用的方法也返回来一个流 Stream<Stream<Character>> streamStream = list.stream().map(Stream3::getchar); //外层的流 streamStream.forEach((e) -> { //传入的e 为内层流 e.forEach((r) -> { System.out.println(r); }); }); } public static Stream<Character> getchar(String string){ List<Character> list = new ArrayList<>(); char[] chars = string.toCharArray(); for (char aChar : chars) { list.add(aChar); } return list.stream(); } //这时候我们就可以看到map方法对于这种操作的已经有一些麻烦了 //这时候我们可以考虑使用flatMap, @Test public void test3(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee"); list.stream().flatMap(Stream3::getchar).forEach(System.out::println); //结果是与上面是一样一样的 } /** * map 与 flatMap 的区别 * map是把每个源数据中的每个元素进行操作,map是一个大的流,那么每个元素就是一个个的小流,map是把每个小流加入到大流中 * {{a,a,a},{b,b,b},{c,c,c}} * flatMap是把源数据中的每个元素中的子元素进行操作,flatMap是一个大流,那么他会把每个子元素加入自己的大流中 * {a,a,a,b,b,b,c,c,c} * * map 就类似与List中的add(Object obj)方法, * flatMap就类似List中的 addAll(Collection con)方法 */ }
排序
package StreamP; import LambdaP.Employee; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Stream4 { /** * 排序 * sorted()-自然排序(Comparable) * sorted(Comparator com)-定制排序 */ public static List<Employee> employees = Arrays.asList( new Employee("张三", 19, 2000), new Employee("张四", 33, 3000), new Employee("张五", 38, 4000), new Employee("张六", 41, 2500), new Employee("张六", 41, 1500), new Employee("张七", 42, 5500) ); //自然排序 public void test(){ List<String> list = Arrays.asList("a","c","r","d","b","z","1"); list.stream().sorted().forEach(System.out::print);//1abcdrz } //利用上面集合排序,按名字排序,名字相同按工资排序 @Test public void test1(){ employees.stream().sorted((x,y)->{ if(x.getName().equals(y.getName())){ return Integer.compare(x.getSalary(),y.getSalary()); }else { return x.getName().compareTo(y.getName()); } }).forEach(System.out::println); // LambdaP.Employee{name='张七', age=42, salary=5500} // LambdaP.Employee{name='张三', age=19, salary=2000} // LambdaP.Employee{name='张五', age=38, salary=4000} // LambdaP.Employee{name='张六', age=41, salary=1500} // LambdaP.Employee{name='张六', age=41, salary=2500} // LambdaP.Employee{name='张四', age=33, salary=3000} } }
查找与匹配
package StreamP; import LambdaP.Employee; import org.junit.Test; import java.util.Arrays; import java.util.List; import java.util.Optional; public class Stream5 { public static List<Employee> employees = Arrays.asList( new Employee("张四", 33, 3000), new Employee("张五", 38, 4000), new Employee("张六", 41, 2500), new Employee("张三", 19, 2000), new Employee("张六", 41, 1500), new Employee("张七", 42, 5500) ); /** * 查找与匹配 * allMatch-检查是否匹配所有元素 * anyMatch-检查是否匹配至少一个元素 * noneMatch-检查是否没有匹配所有元素 * findFirst-返回第一个元素 * findAny-返回当前流中的任意元素 * count-返回流中总个数 * max-返回流中最大值 * min-返回流中最小值 */ //allMatch测试 public void test(){ boolean b = employees.stream().allMatch((e) -> e.getName().getClass()==String .class); System.out.println(b);//true 检查名字是不是String类型的,那肯定是啊 boolean b1 = employees.stream().allMatch((e)->e.getSalary()==1500); System.out.println(b1);//false 检查工资是否都等于1500,那肯定不是啊 } //anyMatch测试 public void test1(){ boolean zhang = employees.stream().anyMatch((e) -> e.getName().equals("张三")); System.out.println(zhang);//检查是否有一个匹配张三的 true } //noneMatch测试 public void test2(){ boolean b = employees.stream().noneMatch((e) -> e.getSalary() == 1500); System.out.println(b);//false 检查是否没有匹配所有元素,如果判断条件存在于流中那么他就返回false,如果不存在那么就返回true } //findFirst测试 public void test3(){ //Optional 是java 8 中新增的,为了防止空指针异常,别的帖子会解释这个类 Optional<Employee> first = employees.stream().findFirst(); System.out.println(first.get());//LambdaP.Employee{name='张三', age=19, salary=2000} } //findAny测试 public void test4(){ //这里Strem串行流 与 parallelStream并行流 的区别 //Stream时一条线程去找,并行流即几条线程同时去找,谁找到算谁的 Optional<Employee> any = employees.parallelStream().findAny(); System.out.println(any); } //count测试 public void test5(){ long count = employees.stream().count(); System.out.println(count);//6 } //max测试 public void test6(){ //max与min都必须有比较条件才可以 Optional<Employee> max = employees.stream().max((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary())); System.out.println(max.get());//LambdaP.Employee{name='张七', age=42, salary=5500} } //min测试 @Test public void test7(){ Optional<Employee> min = employees.stream().min((e1, e2) -> Integer.compare(e1.getage(), e2.getage())); System.out.println(min.get());//LambdaP.Employee{name='张三', age=19, salary=2000} } }
归约
package StreamP; import LambdaP.Employee; import java.util.Arrays; import java.util.List; import java.util.Optional; public class Stream6 { /** * 归约 * reduce(T identity,BinaryOperator) / reduce(BinaryOperator)--可以将流中的元素反腐结合起来,得到一个值。 */ public static List<Employee> employees = Arrays.asList( new Employee("张四", 33, 3000), new Employee("张五", 38, 4000), new Employee("张六", 41, 2500), new Employee("张三", 19, 2000), new Employee("张六", 41, 1500), new Employee("张七", 42, 5500) ); public void test(){ List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); //求和 Integer reduce = list.stream().reduce(0, (x, y) -> x + y); System.out.println(reduce);//55 /** * reduce第一个参数是一个起始值,第二个参数是一个二元运算, * 这个方法有重载,像上面这个方法的话, * 他每次把值都放到y,第一次x起始值为0,所以就是0+y,0+y的结果再次放到x,然后把下一个元素放到y,在进行运算,以此类推 */ } public void test1(){ //获取工资总和、 //获取employees的流,然后取出每个对象的工资,然后求和 Integer reduce = employees.stream().map(Employee::getSalary).reduce(0, (x, y) -> x + y); System.out.println(reduce);//18500 //*********************************************************************** //sum 定义如下 public static int sum(int a, int b) { // return a + b; // } Optional<Integer> reduce1 = employees.stream().map(Employee::getSalary).reduce(Integer::sum); System.out.println(reduce1.get());//18500这里为啥是返回Optional?因为上一个重载的reduce有起始值,再怎么滴都是有值的,那么这个是没有起始值保证的 } }
收集:!!!!!!!
package StreamP; import LambdaP.Employee; import org.junit.Test; import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; public class Stream7 { /** * 收集: * collect--将流转换为其他形式,接收一个Collector(收集器)接口的实现,用于给Stream中元素做汇总的方法 * 在这里,java为我们提供了Collector接口的工具类 Collectors实现类,里面有很多静态方法可供调用 */ public static List<Employee> employees = Arrays.asList( new Employee("张四", 33, 3000), new Employee("张五", 38, 4000), new Employee("张六", 41, 3000), new Employee("张三", 19, 2000), new Employee("张7", 41, 3000), new Employee("张七", 42, 5500) ); //收集到指定集合中! public void test(){ //需求 ,将名字提取出来,放到List中咋办呢 List<Employee> collect = employees.stream().collect(Collectors.toList()); collect.stream().forEach(System.out::println);//即可输出 System.out.println("***************"); //如果平时我们需要的收集到的集合没有提供相应的办法咋办 HashSet<Employee> collect1 = employees.stream().collect(Collectors.toCollection(HashSet::new)); for (Employee employee : collect1) { System.out.println(employee); } } //可以计算各项数据 public void test1(){ //求总数 Long collect = employees.stream().collect(Collectors.counting()); System.out.println(collect);//6 //求最小 Optional<Employee> collect1 = employees.stream().collect(Collectors.minBy((x, y) -> Integer.compare(x.getSalary(), y.getSalary()))); System.out.println(collect1.get());//LambdaP.Employee{name='张六', age=41, salary=1500} System.out.println("*************************"); //取出工资求工资最小 Optional<Integer> collect2 = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Integer::compare)); System.out.println(collect2.get());//1500 //求最大 Optional<Employee> collect3 = employees.stream().collect(Collectors.maxBy((x, y) -> x.getage() - y.getage())); System.out.println(collect3);//Optional[LambdaP.Employee{name='张七', age=42, salary=5500}]以为没有get,所以外层抱着一个Optional对象 //求平均 Double collect4 = employees.stream().collect(Collectors.averagingInt(Employee::getSalary)); System.out.println(collect4);//3083.3333333333335 这里平均数有三个不同的方法分别是转int,转double,转long,并且后面时自己循环,不需要自己map了 //求和 同样是三个不同方法 转int,转double,转long Double collect6 = employees.stream().collect(Collectors.summingDouble(Employee::getage)); System.out.println(collect6);//214.0 // 同样是三个不同方法 转int,转double,转long,但是这里是按照 指定列 把上面的所有参数都求出来的 DoubleSummaryStatistics collect5 = employees.stream().collect(Collectors.summarizingDouble(Employee::getage)); System.out.println(collect5);//DoubleSummaryStatistics{count=6, sum=214.000000, min=19.000000, average=35.666667, max=42.000000} System.out.println(collect5.getCount());//6 System.out.println(collect5.getMax());//42。0 } //分组!! public void test2(){ //在这我才更改了上面集合的工资数据, Map<Integer, List<Employee>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getSalary)); System.out.println(collect);//下面的数据就是分组后的,Map的key为分组条件,value为List的分组后的对象 //{ // 2000=[LambdaP.Employee{name='张三', age=19, salary=2000}, LambdaP.Employee{name='张六', age=41, salary=2000}], // 4000=[LambdaP.Employee{name='张五', age=38, salary=4000}], // 3000=[LambdaP.Employee{name='张四', age=33, salary=3000}, LambdaP.Employee{name='张六', age=41, salary=3000}], // 5500=[LambdaP.Employee{name='张七', age=42, salary=5500}] // } ConcurrentMap<Integer, List<Employee>> collect1 = employees.stream().collect(Collectors.groupingByConcurrent(Employee::getSalary)); System.out.println(collect1);//还没搞清。。。。 //{ // 4000=[LambdaP.Employee{name='张五', age=38, salary=4000}], // 2000=[LambdaP.Employee{name='张三', age=19, salary=2000}, LambdaP.Employee{name='张六', age=41, salary=2000}], // 3000=[LambdaP.Employee{name='张四', age=33, salary=3000}, LambdaP.Employee{name='张六', age=41, salary=3000}], // 5500=[LambdaP.Employee{name='张七', age=42, salary=5500}] //} } //多级分组!! public void test3(){ //按工资分,工资一样按年龄分 Map<Integer, Map<String, List<Employee>>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getSalary, Collectors.groupingBy((e) -> { if (e.getage() < 20) { return "青年"; } else if (e.getage() < 40) { return "中年"; } else { return "老年"; } }))); System.out.println(collect); //{ // 2000={青年=[LambdaP.Employee{name='张三', age=19, salary=2000}]}, // 4000={中年=[LambdaP.Employee{name='张五', age=38, salary=4000}]}, // 3000={老年=[LambdaP.Employee{name='张六', age=41, salary=3000}, LambdaP.Employee{name='张7', age=41, salary=3000}], 中年=[LambdaP.Employee{name='张四', age=33, salary=3000}]}, // 5500={老年=[LambdaP.Employee{name='张七', age=42, salary=5500}]} //} } //分区 分为true与false public void test4(){ Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy((e) -> e.getage() > 35)); System.out.println(collect); //{ // false=[LambdaP.Employee{name='张四', age=33, salary=3000}, LambdaP.Employee{name='张三', age=19, salary=2000}], // true=[LambdaP.Employee{name='张五', age=38, salary=4000}, LambdaP.Employee{name='张六', age=41, salary=3000}, // LambdaP.Employee{name='张7', age=41, salary=3000}, LambdaP.Employee{name='张七', age=42, salary=5500}] //} } //连接! @Test public void test5(){ //参数:第一个是用什么隔开这些需要连接的字符串,第二个是字符串开头,第三个是字符串结尾 String collect = employees.stream().map(Employee::getName).collect(Collectors.joining(",", "--", "--")); System.out.println("collect = " + collect);//collect = --张四,张五,张六,张三,张7,张七-- } }
Stream API就到此结束了!