8.Collector收集器和集合统计
8.1.collector收集器
- collect()方法的作用
- 一个终端操作,用于对流中的数据进行归集操作,collect方法接收的参数是一个Collector
- 有两个方法重载,在Stream接口里面
//重载方法一 <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R>combiner); //重载方法二 <R, A> R collect(Collector<? super T, A, R> collector)
- Collector的作用
- 就是收集器,也是一个接口,它的工具类Collectors提供了很多工厂方法
- Collectors的作用
- 提供了很多常见的集合的收集
- Collectors.toList()
public static <T> Collector<T, ?, List<T>> toList() { return new CollectorImpl<>( (Supplier<List<T>>) ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, CH_ID ); }
- ArrayList::new,创建一个ArrayList作为累加器
- List::add,对流中的元素直接加到累加器中
- reduce,对子任务归集结果直接全部添加到前一个子任务结果中
- CH_ID是一个unmodifableSet集合
常见的收集器
- Collector.toMaps():创建一个map收集器
- Collector.toSet():创建一个set收集器
- Collector.toCollection():自定义收集器
- Collector.toCollection(LinkedList::new)
- Collector.toCollection(TreeSet::new)
8.2.joining函数
- 拼接函数,将集合内的元素按照一定规则拼接起来,Collectors.joining()
//三种重载方法 Collectors.joining() //无参数默认按照空字符拼接 Collectors.joining("-") //一个参数,按照“-”进行分割 Collectors.joining("-","{","}") //三个参数,第一个为分隔符,后两个为前缀后缀
- 源码
- 测试
List<String> list = Arrays.asList("SpringBoot","Docker","Java","SpringCloud","Netty"); String collect1 = list.stream().collect(Collectors.joining()); System.out.println(collect1); String collect2 = list.stream().collect(Collectors.joining("-")); System.out.println(collect2); String collect3 = list.stream().collect(Collectors.joining("-", "{", "}")); System.out.println(collect3);
8.3.partitioningBy分组
- Collectors.partitioningBy()会根据筛选条件进行分组,返回一个Map<Boolean,List>类型,boolean为true的表示符合条件的,false的为不符合筛选条件的。
- 源码
- 测试
List<String> list = Arrays.asList("Java","SpringBoot","HTML5","CSS3"); Map<Boolean, List<String>> collect = list.stream().collect(Collectors.partitioningBy(obj -> obj.length() > 4)); System.out.println(collect);
8.4.groupingBy分组
- Collectors.groupingBy(),会根据实体Bean的某个属性进行分组,更实用,返回的是Map<String,List>,key为要分组的实体Bean的属性值
- 源码
- 测试
List<Student> students = Arrays.asList( new Student("lx", 23, "北京"), new Student("ls", 24, "天津"), new Student("zs", 23, "⼴东"), new Student("ww", 22, "⼴东"), new Student("ld", 20, "北京"), new Student("xs", 20, "⼴东"), new Student("ok", 25, "北京")); Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince())); //第二种方式 //Map<String, List<Student>> collect1 = students.stream().collect(Collectors.groupingBy(Student::getProvince)); collect.forEach((key,value) -> { System.out.println("----"+key); value.forEach(obj-> System.out.println(obj)); });
8.5.counting函数
- 用于统计groupingBy于分组之后,每组元素的个数
- 源码
- 测试
List<Student> students = Arrays.asList( new Student("lx", 23, "北京"), new Student("ls", 24, "天津"), new Student("zs", 23, "⼴东"), new Student("ww", 22, "⼴东"), new Student("ld", 20, "北京"), new Student("xs", 20, "⼴东"), new Student("ok", 25, "北京")); Map<String, Long> collect1 = students.stream().collect(Collectors.groupingBy(Student::getProvince, Collectors.counting())); collect1.forEach((key,value) -> { System.out.println("---"+key); System.out.println("---"+value); });
8.6.summarizing集合统计
- 集合相关的统计都能实现,根据实体Bean的某个属性进行统计,求最大值,最小值,总和等等
- 分类
- summarizingInt
- summarizingLong
- summarizingDouble
- 测试,统计各个学生的年龄信息
IntSummaryStatistics collect = students.stream().collect(Collectors.summarizingInt(Student::getAge)); System.out.println("平均值:"+collect.getAverage()); System.out.println("总人数:"+collect.getCount()); System.out.println("年龄总和:"+collect.getSum()); System.out.println("最大值:"+collect.getMax()); System.out.println("最小值:"+collect.getMin());
9.Collection和Lambda实战
(1)需求:求两个集合的交集、差集、去重并集,两集合的各自平均值,总和
(2)创建两个集合
//总价 35 List<VideoOrder> videoOrders1 = Arrays.asList( new VideoOrder("20190242812", "springboot教程", 3), new VideoOrder("20194350812", "微服务SpringCloud", 5), new VideoOrder("20190814232", "Redis教程", 9), new VideoOrder("20190523812", "⽹⻚开发教程", 9), new VideoOrder("201932324", "百万并发实战Netty", 9)); //总价 54 List<VideoOrder> videoOrders2 = Arrays.asList( new VideoOrder("2019024285312", "springboot教程", 3), new VideoOrder("2019081453232", "Redis教程", 9), new VideoOrder("20190522338312", "⽹⻚开发教程", 9), new VideoOrder("2019435230812", "Jmeter压⼒测试", 5), new VideoOrder("2019323542411", "Git+Jenkins持续集成", 7), new VideoOrder("2019323542424", "Idea全套教程", 21));
(3)注意一点要重写equals和hashcode方法
@Override public boolean equals(Object o) { if(o instanceof VideoOrder){ VideoOrder o1 = (VideoOrder)o; return this.title.equals(o1.title); } return super.equals(o); } @Override public int hashCode() { return title.hashCode(); }
(4)计算两个集合的交集
List<VideoOrder> clist = videoOrder1.stream().filter(videoOrder2::contains).collect(Collectors.toList());
(5)计算两个集合的差集
List<VideoOrder> nclist = videoOrder1.stream().filter(obj -> !videoOrder2.contains(obj)).collect(Collectors.toList());
(6)计算两个集合的去重并集
List<VideoOrder> allList = videoOrder1.paralleStream().distinct().collect(Collectors.toList()); allList.addAll(videoOrder2);
(7)计算订单价格的平均值
double avg = videoOrder1.stream().collect(Collectors.averagingInt(VideoOrder::getMoney)).doubleValue();
(8)计算订单价格的总和
Integer sum = videoOrders1.stream().collect(Collectors.summingInt(VideoOrder::getMoney));
10.内存空间和异常处理
10.1.新内存空间Matespace
- JVM 种类有很多,⽐如 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM,我们讲的是Hotspot才有,JRockit以及J9是没有这个区域
- JVM内存JDK8之前的HotSpot JVM,有个区域叫做”永久代“(permanent generation),通过在命令行设置参数:-XX:MaxPermSize来设定永久代最大可分配的内存空间
- 如果JDK8中设置了PermSize和Max PermSize会被忽略并且警告
- 作用:该块内存主要是被JVM用来存放class和mate信息的,当class被加载loader的时候会被存储到该内存区中,入方法的编译信息及字节码、常量池和符号解析、类的层级信息,字段、名字等
- java.lang.OutOfMemoryError: PermGen space
- 异常原因:永久代空间不够,可能类太多导致
- JDK8使用本地内存存储类元数据信息,叫做元空间(Metaspcae)
- 在默认情况下Metaspcae的大小至于本地内存大小有关系
- 常⽤的两个参数
- -XX:MetaspaceSize=N 指Metaspace扩容时触发FullGC的初始化阈值
- -XX:MaxMetaspaceSize=N 指⽤于限制Metaspace增⻓的上限,防⽌因为某些情况导致Metaspace⽆限的使⽤本地内存
- 不管两个参数如何设置,都会从20.8M开始,然后随着类加载越来越多不断扩容调整直到最⼤
- 查看MetaspaceSize的大小
- jstat -gc pid
10.2.try-with-resources(JDK7)
- 自动关流
- jdk7之前处理异常
public static void main(String[] args) throws FileNotFoundException { String filePath = "F:\\1.txt"; test(filePath); } private static void test(String filePath) throws FileNotFoundException { OutputStream outputStream = new FileOutputStream(filePath); try { outputStream.write((filePath+"学习").getBytes()); } catch (IOException e) { e.printStackTrace(); }finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
- jdk7之后处理异常
public static void main(String[] args) throws FileNotFoundException { String filePath = "F:\\1.txt"; test(filePath); } private static void test(String filePath) throws FileNotFoundException { //自动关流,当有多个文件时,直接用;分割就可以 try (OutputStream outputStream = new FileOutputStream(filePath)){ outputStream.write((filePath + "学习").getBytes()); } catch (IOException e) { e.printStackTrace(); } }
- 实现了AutoCloseable接口的类,在try()里面声明该类实例的时候,try结束后自动i盗用的close方法,这个动作会早于finally里面调用的方法。
- 不管是否出现异常,try()里面的实例都会被调用close方法。
- try里面可以声明多个自动关闭的对象,越早声明的对象,会越晚被关掉。
public static void main(String[] args) throws FileNotFoundException { String filePath = "F:\\1.txt"; test(filePath); } private static void test(String filePath) throws FileNotFoundException { OutputStream outputStream = new FileOutputStream(filePath); try { outputStream.write((filePath+"学习").getBytes()); } catch (IOException e) { e.printStackTrace(); }finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
- jdk7之后处理异常
public static void main(String[] args) throws FileNotFoundException { String filePath = "F:\\1.txt"; test(filePath); } private static void test(String filePath) throws FileNotFoundException { //自动关流,当有多个文件时,直接用;分割就可以 try (OutputStream outputStream = new FileOutputStream(filePath)){ outputStream.write((filePath + "学习").getBytes()); } catch (IOException e) { e.printStackTrace(); } }
- 实现了AutoCloseable接口的类,在try()里面声明该类实例的时候,try结束后自动i盗用的close方法,这个动作会早于finally里面调用的方法。
- 不管是否出现异常,try()里面的实例都会被调用close方法。
- try里面可以声明多个自动关闭的对象,越早声明的对象,会越晚被关掉。