JDK8新特性(二)

一、方法引用与构造函数引用

  • 说明:方法引用是⼀种更简洁易懂的lambda表达式,操作符是双冒号::,用来直接访问类或者实例已经存在的方法或构造方法
  • 通过方法引用,可以将方法的引⽤赋值给⼀个变量
  • 语法:左边是容器(可以是类名,实例名),中间是" :: ",右边是相应的方法名
    1. 静态方法,则是ClassName::methodName。如 Object ::equals
    2. 实例方法,则是Instance::methodName
    3. 构造函数,则是 类名::new;
  • 单个参数
    Function<入参1, 返回类型> func = 方法引用
    应用 func.apply(入参);
  • 两个参数
    BiFunction<入参1,入参2, 返回类型> func = 方法引用
    应用 func.apply(入参1,入参2);
class User {
    private String username;
    private Integer age;
    public User() {}
    public User(String username) {
        this.username = username;
    }
    public User(String username, Integer age) {
        this.username = username;
        this.age = age;
    }
 }

public class Test{

    public static void main(String[] args) {
        // 使用双冒号::来构造静态函数引用
        Function<String, Integer> fun = Integer::parseInt;
        Integer value = fun.apply("1024");
        System.out.println(value);
        // 使用双冒号::来构造非静态函数引用
        String content = "测试使用";
        Function<Integer, String> func = content::substring;
        String result = func.apply(1);
        System.out.println(result);
        // 构造函数引用,多个参数
        BiFunction<String, Integer, User> biFunction = User::new;
        User user1 = biFunction.apply("Test", 28);
        System.out.println(user1.toString());
        //构造函数引用,单个参数
        Function<String, User> function = User::new;
        User user2 = function.apply("Test");
        System.out.println(user2.toString());
        // 函数引用也是一种函数式接用,可以将函数引用作为方法的参数
        sayHello(String::toUpperCase, "Test");
    }
    
    /**
     *
     * @param func 函数引用
     * @param param 对应的参数
     */
    private static void sayHello(Function<String, String> func, Stringparam) {
        String result = func.apply(param);
        System.out.println(result);
    }

}

二、JDK8之流Stream

  • Stream 中文称为 “流”,通过将集合转换为这么⼀种叫做 “流”的元素队列,通过声明性方式,能够对集合中的每个元素进行⼀系列并行或串行的流水线操作。
  • 元素是特定类型的对象,所以元素集合看作⼀种流, 流在管道中传输, 且可以在管道的节点上进行处理, ⽐如 排序,聚合,过滤等操作。


    image.png
  • 操作详情
    1. 数据元素便是原始集合,如List、Set、Map等
    2. 生成流,可以是串行流stream() 或者并行流 parallelStream()
    3. 中间操作,可以是 排序,聚合,过滤,转换等
    4. 终端操作,很多流操作本身就会返回⼀个流,所以多个操作可以直接连接起来,最后统⼀进行收集
List<String> list = Arrays.asList("aaaa","bbbb","cccc","dddd","eeee);
List<String> resultList = list.stream().map(obj->"遍历:"+obj).collect(Collectors.toList());
System.out.println(resultList);

三、JDK8之流操作map和filter函数

  • map函数
    1. 将流中的每⼀个元素 T 映射为 R(类似类型转换)
    2. 类似遍历集合,对集合的每个对象做处理
    3. 场景:转换对象,如javaweb开发中集合里面的DO对象转换为DTO对象
List<String> list = Arrays.asList("aaaa","bbbb","cccc","dddd","eeee);
List<String> resultList = list.stream().map(obj->"遍历:"+obj).collect(Collectors.toList());
System.out.println(resultList);
@Data
class User {
    private String name;
    private Integer age;
    public User(String name, Integer age) {
        this.name= name;
        this.age = age;
    }
}

@Data
class UserDTO {
    private String name;
    private Integer age;
    public User(String name, Integer age) {
        this.name= name;
        this.age = age;
    }
}

public class Test{
    public static void main(String[] args) {
        List<User> list = Arrays.asList(new User("peter",11),new User("jack",12),new User("tom",10),new User("marry",14));
        List<UserDTO> userDTOList = list.stream().map(obj->{
            UserDTO userDTO = new UserDTO(obj.getName(),obj.getAage());
            return userDTO;
        }).collect(Collectors.toList());
        System.out.println(userDTOList);
    }
}
  • filter函数
    1. 用于通过设置的条件过滤出元素
    2. 场景:主要用于筛选过滤出符合条件的元素
List<String> list = Arrays.asList("springboot", "springcloud","redis", "git", "netty", "java", "html", "docker");
List<String> resultList = list.stream().filter(obj -> obj.length() >5).collect(Collectors.toList());
System.out.println(resultList);

四、JDK8之流操作limit和sorted函数

  • limit函数
    1. 截断流使其最多只包含指定数量的元素
List<String> list = Arrays.asList("springboot", "springcloud","redis", "git", "netty", "java", "html", "docker");
//limit截取
List<String> resultList=list.stream().sorted(Comparator.comparing(String::length).reversed()).limit(3).collect(Collectors.toList());
System.out.println(resultList);
  • sorted函数
    1. sorted() 对流进⾏自然排序, 其中的元素必须实现Comparable 接口
    2. sorted(Comparator<? super T> comparator) 用来自定义升降序
List<String> list = Arrays.asList("springboot", "springcloud","redis", "git", "netty", "java", "html", "docker");
List<String> resultList1 = list.stream().sorted().collect(Collectors.toList());
List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList());
List<String> resultList3 = list.stream().sorted(Comparator.comparing(obj ->obj.length(),Comparator.reverseOrder())).collect(Collectors.toList());
List<String> resultList4 = list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.toList());

五、JDK8之流操作allMatch和anyMatch函数

  • allMatch函数
    1. 检查是否匹配所有元素,只有全部符合才返回true
List<String> list = Arrays.asList("springboot", "springcloud", "redis","git", "netty", "java", "html", "docker");
boolean flag = list.stream().allMatch(obj->obj.length()>1);
System.out.println(flag);
  • anyMatch函数
    1. 检查是否⾄少匹配⼀个元素
List<String> list = Arrays.asList("springboot", "springcloud", "redis","git", "netty", "java", "html", "docker");
boolean flag = list.stream().anyMatch(obj->obj.length()>18);
System.out.println(flag)

六、JDK8之流操作max和min函数

  • max和min函数
  1. 最大值和最小值
@Data
class Student {
    private int age;
    public Student(int age) {
        this.age = age;
    }
}

public class Test{
    public static void main(String[] args) {
        List<Student> list = Arrays.asList(new Student(32),newStudent(33),new Student(21),new Student(29),new Student(18));
        //最大值
        Optional<Student> optional = list.stream().max((s1, s2)->Integer.compare(s1.getAge(),s2.getAge()));
        //最小值
        Optional<Student> optional = list.stream().min((s1, s2)->Integer.compare(s1.getAge(),s2.getAge()));
        System.out.println(optional.get().getAge());
    }
}

七、JDK8之并行流parallelStream

  • 为什么会有这个并行流
    1. 集合做重复的操作,如果使⽤串行执行会相当耗时,因此⼀般会采用多线程来加快, Java8的paralleStream用fork/join框架提供了并发执行能力。
    2. 底层原理
      (1) 线程池(ForkJoinPool)维护⼀个线程队列
      (2) 可以分割任务,将父任务拆分成子任务,完全贴合分治思想
//顺序输出
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.stream().forEach(System.out::println);
//并⾏乱序输出
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream().forEach(System.out::println);
  • paralleStream并行是否⼀定比Stream串行快?
    1. 错误,数据量少的情况,可能串行更快,ForkJoin会耗性能
    2. 多数情况下并行比串行快,是否可以都用并行
      (1) 不行,部分情况会有线程安全问题,parallelStream里⾯使用的外部变量,比如集合⼀
      定要使用线程安全集合,不然就会引发多线程安全问题。
for(int i=0;i<10;i++) {
     //List list = new ArrayList();
    List list = new CopyOnWriteArrayList();
     IntStream.range(0, 100).parallel().forEach(list::add);
     System.out.println(list.size());
 }
image.png

image.png

八、JDK8之reduce操作

  • 什么是reduce操作

    1. 聚合操作,中文意思是 “减少”
    2. 根据⼀定的规则将Stream中的元素进行计算后返回⼀个唯⼀的值
  • 常用方法一

Optional<T> reduce(BinaryOperator<T> accumulator);

//accumulator 计算的累加器
//例子: 第⼀个元素相加和第⼆个元素相加,结果再和第三个元素相加,直到全部相加完成
int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1+ item2).get();

//不⽤lambda的写法
int result = Stream.of(1,2,3,4,5).reduce(new BinaryOperator<Integer>() {
    @Override
    public Integer apply(Integer item1, Integer item2) {
        return item1 + item2;
    }
 }).get();
  • 常用方法二
T reduce(T identity, BinaryOperator<T> accumulator);

//identity ⽤户提供⼀个循环计算的初始值
//accumulator 计算的累加器
//例子: 100作为初始值,然后和第⼀个元素相加,结果在和第⼆个元素相加,直到全部相加完成
int value = Stream.of(1, 2, 3, 4,5).reduce(100, (sum, item) -> sum + item);
  • 例子应用
int value = Stream.of(1645, 234345, 32,44434,564534,435,34343542,212).reduce( (item1, item2) -> item1 > item2 ? item1 : item2 ).get();
System.out.println(value);

九、JDK8之集合的foreach

  • 集合遍历的方式
    1. for循环
    2. 迭代器 Iterator
  • 注意点
    1. 不能修改包含外部的变量的值
    2. 不能用break或者return或者continue等关键词结束或者跳过循环
  • Jdk8里面的新增接口
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
  • 使用案例
List<Student> results = Arrays.asList("aaaa","bbbb","ccccc","ddddd","eeeee");
results.forEach(obj->{System.out.println(obj.toString());});

十、JDK8之collector收集器

  • collect()方法的作用
    1. ⼀个终端操作, 用于对流中的数据进行归集操作,collect方法接受的参数是⼀个Collector
    2. 有两个重载方法,在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的作用
    1. 就是收集器,也是⼀个接口, 它的工具类Collectors提供了很多工厂方法
  • Collectors 的作用
    1. 工具类,提供了很多常见的收集器实现
      (1)Collectors.toList()
//ArrayList::new,创建⼀个ArrayList作为累加器
// List::add,对流中元素的操作就是直接添加到累加器中reduce操作, 对⼦任务归集结果addAll,后⼀个子任务的结果直接全部添加到前⼀个子任务结果中
// CH_ID 是⼀个unmodifiableSet集合
 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);
}
  1. Collectors.toMap()
  2. Collectors.toSet()
  3. Collectors.toCollection() :⽤⾃定义的实现Collection的数据结构收集
Collectors.toCollection(LinkedList::new)
Collectors.toCollection(CopyOnWriteArrayList::new)
Collectors.toCollection(TreeSet::new)

十一、JDK8之joining函数

  • 拼接函数 Collectors.joining
//3种重载⽅法
Collectors.joining()
Collectors.joining("param")
Collectors.joining("param1", "param2", "param3")
  • 其中⼀个的实现
public static Collector<CharSequence, ?, String> joining() {
    return new CollectorImpl<CharSequence, StringBuilder, String>(
        StringBuilder::new, StringBuilder::append,(r1, r2) -> { r1.append(r2); return r1; },StringBuilder::toString, CH_NOID);
 }
  • 该方法可以将Stream得到⼀个字符串, joining函数接受三个参数,分别表示 元素之间的连接符、前缀和后缀。
String result = Stream.of("springboot", "mysql", "html5","css3").collect(Collectors.joining(",", "[", "]"));

十二、JDK8之收集器 partitioningBy分组

  • Collectors.partitioningBy 分组,key是boolean类型
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? superT> predicate) {
    return partitioningBy(predicate, toList());
}
  • 例子: 根据list里面进行分组,字符串长度大于4的为⼀组,其他为另外⼀组
List<String> list = Arrays.asList("java", "springboot","HTML5","nodejs","CSS3");
Map<Boolean, List<String>> result = list.stream().collect(partitioningBy(obj -> obj.length() > 4));

十三、JDK8之收集器 group by分组

  • 分组 Collectors.groupingBy()
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<?super T, ? extends K> classifier) { 
    return groupingBy(classifier, toList());
}
  • 例子:根据学生所在的省份,进行分组
@Data
class Student {
    private String province;
    private int age;
    public Student(String province, int age) {
       this.age = age;
       this.province = province;
    }
}

public class Test{
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(new Student("广东", 23), newStudent("广东", 24), new Student("广东", 23),new Student("北京", 22), new Student("北京", 20), new Student("北京", 20),new Student("海南", 25));
        Map<String, List<Student>> listMap = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince()));
        listMap.forEach((key, value) -> {
            value.forEach(obj -> {
                System.out.println(obj.getAge());
            });
        });
    }
}

十四、JDK8之summarizing集合统计

  • summarizing 统计相关, 下面是summarizingInt的源码
  • 作用:可以⼀个方法把统计相关的基本上都完成
  • 分类
    1. summarizingInt
    2. summarizingLong
    3. summarizingDouble
public static <T> Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) { 
    return new CollectorImpl<T, IntSummaryStatistics, IntSummaryStatistics>(IntSummaryStatistics::new,(r, t) -> r.accept(mapper.applyAsInt(t)),(l, r) -> { l.combine(r); return l; }, CH_ID);
}
  • 例子:统计学生的各个年龄信息
@Data
class Student {
    private String province;
    private int age;
    public Student(String province, int age) {
       this.age = age;
       this.province = province;
    }
}

public class Test{
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(new Student("广东", 23), newStudent("广东", 24), new Student("广东", 23),new Student("北京", 22), new Student("北京", 20), new Student("北京", 20),new Student("海南", 25));
        IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));
        System.out.println("平均值:" + summaryStatistics.getAverage());
        System.out.println("人数:" + summaryStatistics.getCount());
        System.out.println("最大值:" + summaryStatistics.getMax());
        System.out.println("最小值:" + summaryStatistics.getMin());
        System.out.println("总和:" + summaryStatistics.getSum());
    }
}

十五、 JDK8 新的内存空间和异常处理

  • JVM 种类有很多,比如 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM,我们讲的是Hotspot才有,JRockit以及J9是没有这个区域。
  • JVM内存知识 在JDK8之前的HotSpot JVM,有个区域叫做“永久代(permanent generation), 通过在命令行设置参数-XX:MaxPermSize来设定永久代最大可分配的内存空间,如果JDK8⾥⾯设置了PermSize 和MaxPermSize 会被忽略并给出警告。
  • 作用:该块内存主要是被JVM⽤来存放 class 和 mate 信息的,当 class 被加载 loader 的时候就会被存储到该内存区中,如方法的编译信息及字节码、常量池和符号解析、类的层级信息,字段,名字等。
  • 这个异常应该熟悉 java.lang.OutOfMemoryError: PermGen space,原因是: 永久代空间不够,类太多导致
  • jdk8的修改 JDK8 HotSpot JVM 使用本地内存来存储类元数据信息,叫做 元空间(Metaspace),在默认情况下Metaspace的大小只与本地内存大小有关。
  • 常用的两个参数 -XX:MetaspaceSize=N 指Metaspace扩容时触发FullGC的初始化阈值-XX:MaxMetaspaceSize=N 指用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存。
  • 不管两个参数如何设置,都会从20.8M开始,然后随着类加载越来越多不断扩容调整直到最大。
  • 查看大小 jstat -gc pid MC: current metaspace capacity MU: mateaspace utilization 单位是KB。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容