IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Java8 新特性 -> 正文阅读

[数据结构与算法]Java8 新特性

1、Java 8 新特性简介

主要内容

  1. Lambda表达式
  2. 函数式接口
  3. 方法引用与构造器引用
  4. Stream API
  5. 接口中的默认方法与静态方法
  6. 新时间日期API
  7. 其他新特性

特点

  • 速度更快。对于底层的数据结构做了更新与改动,垃圾回收机制内存结构做了一定的改变。
  • 代码更少(增加了新的语法Lambda表达式)
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常 Optional
    其中最为核心的就是lambda表达式与Stream API

底层数据结构的更改
1、对于顶层数据结构最核心的体现,HashMap。
原来的HashMap数据存储的结构 数组 + 链表
HashMap底层实际上是一个16位的数组,数组的值为entry。存储对象时先通过hash算法计算出对象所对应的数组索引值,然后在通过equals方法比较该索引下是否已有相同的对象,有则覆盖原来的对象。没有则放在原有对象的前面,与原来的对象形成一个链表,这种情况我们称之为碰撞。碰撞会使我们存储的效率变低,我们需要重写hashCode与equals方法来尽量避免这种碰撞。当HashMap中元素存储的数量过大,会降低我们的存储的效率,HashMap为其提供加载因子。当元素到达现有Hash表的75%的时候,即会对其进行扩容。原来的元素就会进行重新运算,存储到扩容后的空间内,这样就减小了元素碰撞的几率。

Java 8以后HashMap数据存储的结构 数组 + 链表 + 红黑树
Java8以后在原来的存储结构下增加了红黑树的结构,我们知道在元素发生碰撞,即多个元素被存储到一个索引下时会时会形成链表。当链表元素的数量到8且整个HashMap存储的元素数量的大于64时,会将原来的链表转换为红黑树。红树。在添加时会将元素进行比较,并将较大的元素添加到元素的左边。这种结构增强了除添加以外的其它所有操作的效率。且在Hash表扩容时,原来的元素不在不需要经过复杂的排序。只需要找原来Hash表的总长度,加上它当前所在的索引位置。

2、Lambda表达式

Lambda是一个匿名函数,我们可以将Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到提升。
先看看以下示例来感受一下Java8的Lambda表达式与其他新特性的独特之处吧

public class TestLambda {
    //原来的匿名内部类
    @Test
    public void t1() {
        Comparator<Integer> comparator = new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
    }

    //Lambda表达式
    @Test
    public void t2() {
        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);//简化了函数式接口的实现
    }

    List<Emp> empList = asList(
            new Emp("张三", 18, 100.00),
            new Emp("李四", 20, 200.00),
            new Emp("王五", 24, 300.00),
            new Emp("赵六", 14, 400.00)
    );

    //需求:获取年龄大于等于18的员工
    //定义过滤方法
    public List<Emp> filterEmployee(List<Emp> list,MyPredicate<Emp> mp){
        List<Emp> emps = new ArrayList<Emp>();
        for (Emp emp : empList) {
            if(mp.test(emp)){
                emps.add(emp);
            }
        }
        return emps;
    }

    @Test
    public void t3() {
        System.out.println("实现方案一:接口实现类");
        List<Emp> emps = filterEmployee(empList,new FilterEmployeeByAge());
        for (Emp emp : emps) {
            System.out.println(emp);
        }

        System.out.println("实现方案二:匿名内部类");
        List<Emp> emps1 = filterEmployee(empList, new MyPredicate<Emp>() {
            @Override
            public boolean test(Emp emp) {
                return emp.getAge() >= 18;
            }
        });
        for (Emp emp : emps1) {
            System.out.println(emp);
        }

        System.out.println("实现方案三:lambda表达式");
        List<Emp> emps2 = filterEmployee(empList, (e) -> e.getAge() >= 18);
        emps2.forEach(System.out::println);

        System.out.println("实现方案四:Stream API");
        empList.stream()
                .filter((e)->e.getAge()>=19)
                .limit(2)
                .forEach(System.out::println);

        empList.stream()
                .map(Emp::getName)
                .forEach(System.out::println);
    }
}

public interface MyPredicate<T> {
    boolean test(T t);
}

public class FilterEmployeeByAge implements MyPredicate<Emp>{
    @Override
    public boolean test(Emp emp) {
        return emp.getAge() >= 18;
    }
}

以上示例是不是让你感受到Lambda表达式与Stream API的强大,想要继续向下了解,接下来讲解的是Lambda表达式的相关知识。

Lambda表达式的基础语法:Java8中引入了一个新的操作符"->"该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式分为两部分:
左侧:Lambda表达式的参数列表。
右侧:Lambda表达式中所需执行的功能,即Lambda体

//语法格式一:无参无返回值
() -> System.out.println("Hello Lambda");

//语法格式二:有一个参数,无返回值
(x) -> System.out.println(x);

//语法格式三:若只有一个参数,小括号可以省略
x -> System.out.println(x);

//语法格式四:有两个以上的参数,有返回值,且Lambda中有多条语句
Comparator<Integer> com = (x, y) -> {
    System.out.println("函数式接口");
    return Integer.compare(x,y);
};

//语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略
Comparator<Integer> com = (x, y) -> return Integer.compare(x,y);

//语法格式六:Lambda表达式的参数列表的数据类型都可以不写,因为JVM编译器通过上下文推断出,数据类型,即"类型推断"
(Integer x,Integer y) -> Integer.compare(x,y);

Lambda函数式接口
Lambda表达式需要函数式接口的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。根据规范需要使用@FuncatinalInterface修饰函数式接口,该接口可以用于检查接口是否是函数式接口。

Java8 内置的四大核心函数式接口

Consumer<T>:消费型接口
 void accept(T t);

Supplier<T>:供给型接口
 T get();
          
Function<T>:函数型接口
 R apply(T t);
          
Predicate<T>:断言型接口
 boolean test(T t);

其他接口

在这里插入图片描述

方法引用与构造器引用
1、方法引用:若Lambda体中的内容已经有别的方法实现了,我们可以使用"方法引用"引用已有的方法。

//方法引用主要的三种语法格式:
//对象::实例方法名
Consumer<String> sup = System.out::println;

//类::静态方法名
Comparator<Integer> sup2 = Integer::compare;

//类::实例方法名
BiPredicate<String,String> b = String::equals;

方法引用的规则:
(1)Lambda体中调用方法的参数列表与返回值类要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
(2)若Lambda参数列表中的第一参数是实例方法的调用者,而第二参数是实例方法的参数时,可以使用ClassName::method。

2、构造器引用

//对象构造器引用 语法:ClassName::new;
Supplier<Emp> sup = Emp::new;
//数组构造器引用 语法:Type[]::new;
Function<Integer,String[]> fun = String[]::new;

注意:使用构造器引用时构造器参数列表要符合函数式接口抽象方法的参数列表保持一致。

3、Stream API

了解Stream
Java8中两个最为重要的改变一个是Lambda表达式,一个就是Stream API(java.util.stream.*)了。
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似与使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。
Stream流到底是什么,其实它是一种数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合是元素,流的作用就是对元素进行计算。

Stream流使用时注意事项:
(1)Stream自己不会存储元素。
(2)Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
(3)Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream使用步骤:创建(获取流) >>> 中间操作(处理流) >>> 终止操作(执行流)
创建Stream流
1.通过Collection 系列集合提供的stream()或parallelStream()

List<String> list = new ArrayList<String>();
Stream<String> stream1 = list.stream();

2.通过Arrays 中的静态方法stream()获取数组流

Emp[] emps = new Emp[10];
Stream<Emp> stream2 = Arrays.stream(emps);

3.通过Stream类中的静态方法 of()

Stream<String> stream3 = Stream.of("aa", "bb", "cc");

4.创建无限流

Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);
stream4.limit(10).forEach(System.out::println);
//生成
Stream.generate(()->Math.random())
        .forEach(System.out::println);

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

筛选与切面操作

方法描述
filter(Predicate p)提供一个断言型接口实现,获取返回值为ture的元素
distiinct()筛选,通过流所生成元素的hashCode()与equals()方法去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量。
skip(long n)跳过元素,返回一个扔掉的前n个元素的流。若流中元素不足n个,则返回一个空流,与limit(n)互补

排序

方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator comp产生一个新流,其中按比较器顺序排序
示例:
public void t2() {
    int[] arr = {4,3,5,6};
    Arrays.stream(arr)
            .sorted()
            .forEach(System.out::println);

    List<Emp> empList = Arrays.asList(
            new Emp("张三", 18, 100.00),
            new Emp("李四", 20, 200.00),
            new Emp("王五", 24, 500.00),
            new Emp("王五", 24, 600.00),
            new Emp("赵六", 14, 400.00)
    );
    Stream<Emp> sorted = empList.stream()
            .sorted((e1, e2) -> {
                if (e1.getAge().equals(e2.getAge())) {
                    return e1.getSalary().compareTo(e2.getSalary());
                }
                return e1.getAge().compareTo(e2.getAge());
            });
    sorted.forEach(System.out::println);
}

映射

方法描述
map(Funcation f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每一个值都转换为另一个流,并将所有流连接成一个流。

Stream的终止操作

查找与匹配

方法描述
allMatch检查是否匹配所有元素
anyMatch检查是否至少匹配一个元素
noneMatch检查是否没有匹配的元素
findFirst返回第一个元素
findAny返回当前流中的任意元素
count返回流中元素的总个数
max返回流中最大值
min返回六中最小值
forEach(Consumer c)内部迭代(使用Collection接口需要用户去做迭代,称为外部迭代。相反,Stream API使用内部迭代——它帮你把迭代做了)
示例:
@Test
public void t3() {
    List<Emp> empList = Arrays.asList(
            new Emp("张三", 18, 100.00,Status.FREE),
            new Emp("李四", 20, 200.00,Status.BUSY),
            new Emp("王五", 24, 500.00,Status.VOCATION),
            new Emp("王五", 24, 600.00,Status.FREE),
            new Emp("赵六", 14, 400.00,Status.BUSY)
    );
    boolean b = empList.stream()
            .noneMatch(e -> e.getStatus().equals(Status.VOCATION));
    System.out.println(b);
}

归纳(汇总)

方法描述
reduce(T iden,BinaryOperator b)可将流中的元素反复结合,得到一个值,返回T
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回Optional<T>

备注:map与reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜素而出名。

示例:
@Test
public void t4() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
    Integer sum = list.stream()
            .reduce(0, Integer::sum);
	System.out.println(sum);


    List<Emp> empList = Arrays.asList(
        new Emp("张三", 18, 100.00,Status.FREE),
        new Emp("李四", 20, 200.00,Status.BUSY),
        new Emp("王五", 24, 500.00,Status.VOCATION),
        new Emp("王五", 24, 600.00,Status.FREE),
        new Emp("赵六", 14, 400.00,Status.BUSY)
	);
    Optional<Double> op = empList.stream()
            .map(Emp::getSalary)
            .reduce(Double::sum);
    System.out.println(op.get());
}

收集

方法描述
collect(Collector c)将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法,可以方便的创建常见收集器实例,具体方法如下表。

示例:
List<Emp> empList = Arrays.asList(
        new Emp("张三", 18, 100.00,Status.FREE),
        new Emp("李四", 36, 200.00,Status.BUSY),
        new Emp("王五", 24, 500.00,Status.VOCATION),
        new Emp("王五", 24, 600.00,Status.FREE),
        new Emp("赵六", 14, 400.00,Status.BUSY)
);

@Test
public void t5() {
    List<String> list1 = empList.stream()
            .map(Emp::getName)
            .collect(Collectors.toList());
    System.out.println(list1);

    HashSet<String> list2 = empList.stream()
            .map(Emp::getName)
            .collect(Collectors.toCollection(HashSet::new));
    System.out.println(list2);

    //平均值
    Double avg = empList.stream()
            .collect(Collectors.averagingDouble(Emp::getSalary));

    //分组
    Map<Status, List<Emp>> map = empList.stream()
            .collect(Collectors.groupingBy(Emp::getStatus));
    System.out.println(map.get(Status.BUSY));

    //多级分组
    Map<Status, Map<String, List<Emp>>> statusMapMap = empList.stream()
            .collect(Collectors.groupingBy(Emp::getStatus, Collectors.groupingBy(
                    e -> {
                        if (e.getAge() <= 35) {
                            return "青年";
                        } else if (e.getAge() <= 50) {
                            return "中年";
                        }
                        return "老年";
                    }
            )));
    Map<String, List<Emp>> stringListMap = statusMapMap.get(Status.BUSY);
    List<Emp> qn = stringListMap.get("青年");
    List<Emp> zn = stringListMap.get("中年");
    System.out.println("请年:" + qn);
    System.out.println("中年:" + zn);

    //分区
    Map<Boolean, List<Emp>> partitionBySalary = empList.stream()
            .collect(Collectors.partitioningBy(e -> e.getSalary() > 300));
    System.out.println("条件为真的:" + partitionBySalary.get(true));;
    System.out.println("条件为假的:" + partitionBySalary.get(false));;

    //统计
    DoubleSummaryStatistics doubleSummaryStatistics = empList.stream()
            .collect(Collectors.summarizingDouble(Emp::getSalary));
    System.out.println(doubleSummaryStatistics.getMax());
    System.out.println(doubleSummaryStatistics.getCount());
    System.out.println(doubleSummaryStatistics.getAverage());

    //连接
    String str = empList.stream()
            .map(Emp::getName)
            .collect(Collectors.joining(","));
    System.out.println(str);
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-12-02 17:01:12  更:2021-12-02 17:03:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 14:58:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码