【Java基础】JDK8新特性最佳实践3

简介: 【Java基础】JDK8新特性最佳实践3

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("-","{","}") //三个参数,第一个为分隔符,后两个为前缀后缀
  • 源码

baefce4b36d74d30bd71266ad09e052d.jpeg

38641759913942099f90e2c27584dc07.jpeg

319d759ec4f64e4983b8ae8f6cdc21f5.jpeg

  • 测试
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);

f06592ff01bd4551a2af247a7a7a7583.jpeg

8.3.partitioningBy分组

  • Collectors.partitioningBy()会根据筛选条件进行分组,返回一个Map<Boolean,List>类型,boolean为true的表示符合条件的,false的为不符合筛选条件的。
  • 源码

 b5e617ce444e4955933cc39c7de82be3.jpeg  

  • 测试
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);

3bbf4f38f7954a25ad865e3d8d7ed5e6.jpeg

8.4.groupingBy分组

  • Collectors.groupingBy(),会根据实体Bean的某个属性进行分组,更实用,返回的是Map<String,List>,key为要分组的实体Bean的属性值
  • 源码

dbebeb6b6c1d4bd7bb5323c8fa25e705.jpeg

d79fe376a7664616898599f8ea3a2bdd.jpeg

  • 测试
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));
        });

8a4680ddcaaf40848476f82be41014cb.jpeg

8.5.counting函数

  • 用于统计groupingBy于分组之后,每组元素的个数
  • 源码

7ab02017a45645eab9d7b28ba8885a3f.jpeg

  • 测试
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);
});

55944b8c2806440bbd3f5c0cde14b641.jpeg

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());

cf3cb39e42a54fc689bba85786f3a31a.jpeg

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

c93322dd4d1a4640a5282719775d07fd.jpeg

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里面可以声明多个自动关闭的对象,越早声明的对象,会越晚被关掉。
相关文章
|
18天前
|
存储 Java 关系型数据库
高效连接之道:Java连接池原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。频繁创建和关闭连接会消耗大量资源,导致性能瓶颈。为此,Java连接池技术通过复用连接,实现高效、稳定的数据库连接管理。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接池的基本操作、配置和使用方法,以及在电商应用中的具体应用示例。
37 5
|
8天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
20 4
|
6天前
|
Java 数据库连接 开发者
Java中的异常处理机制及其最佳实践####
在本文中,我们将探讨Java编程语言中的异常处理机制。通过深入分析try-catch语句、throws关键字以及自定义异常的创建与使用,我们旨在揭示如何有效地管理和响应程序运行中的错误和异常情况。此外,本文还将讨论一些最佳实践,以帮助开发者编写更加健壮和易于维护的代码。 ####
|
10天前
|
Java
Java 异常处理下篇:11 个异常处理最佳实践
本文深入探讨了 Java 异常处理的最佳实践,包括早抛出晚捕获、只捕获可处理的异常、不要忽略捕获的异常、抛出具体检查性异常、正确包装自定义异常、记录或抛出异常但不同时执行、避免在 `finally` 块中抛出异常、避免使用异常进行流程控制、使用模板方法处理重复的 `try-catch`、尽量只抛出与方法相关的异常以及异常处理后清理资源。通过遵循这些实践,可以提高代码的健壮性和可维护性。
|
8天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
33 1
|
11天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
26 2
|
16天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
33 2
|
23天前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
45 3
|
23天前
|
XML JSON 监控
告别简陋:Java日志系统的最佳实践
【10月更文挑战第19天】 在Java开发中,`System.out.println()` 是最基本的输出方法,但它在实际项目中往往被认为是不专业和不足够的。本文将探讨为什么在现代Java应用中应该避免使用 `System.out.println()`,并介绍几种更先进的日志解决方案。
46 1
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。