JDK8新特性

文章目录

  • Stream
    • 一、集合处理数据的弊端
    • 二、Steam流式思想概述
    • 三、Stream流的获取方式
      • ①、根据Collection获取
      • ②、Stream.of方法
    • 四、Stream常用方法
      • ①、forEach
      • ②、count
      • ③、filter
      • ④、limit
      • ⑤、skip
      • ⑥、map
      • ⑦、sorted
      • ⑧、distinct
      • ⑨、match
      • ⑩、reduce
      • ⑪、concat
      • ⑫、find
      • ⑬、 max和min
      • ⑭、mapToInt
      • ⑮、map和reduce的组合
      • ⑯、综合案例
    • 五、Stream结果收集
      • ①、结果收集到集合中
      • ②、结果收集到数组中
      • ③、对流中的数据做聚合计算
      • ④、对流中数据做分组操作
      • ⑤、对流中的数据做分区操作
      • ⑥、对流中的数据做拼接
    • 六、并行的Stream流
      • ①、串行的Stream流
      • ②、并行流
        • Ⅰ、获取并行流
        • Ⅱ、并行流操作
        • Ⅲ、并行流和串行流对比
        • Ⅳ、线程安全问题
  • Optional
    • 一、什么是Optional
    • 二、创建方式
      • ①、of
      • ②、ofNullable
      • ③、empty
    • 三、常用方法
      • ①、get
      • ②、isPresent()
      • ③、orElse(T t)
      • ④、orElseGet(Supplier s)
  • Lambda表达式方法引用
    • 一、为什么要用方法引用
    • 二、方法引用的五种方式
      • ①、对象名::方法名
      • ②、类名::静态方法名
      • ③、类名::引用实例方法
      • ④、类名::构造器
      • ⑤、数组::构造器

Stream

一、集合处理数据的弊端

当我们在需要对集合中的元素进行操作的时候,除了必需的添加,删除,获取外,最典型的操作就是集合遍历,看一段代码

public static void main(String[] args) {
    // 定义一个List集合
    List<String> list = Arrays.asList("张三", "张三丰", "成龙", "周星驰");
    // 1.获取所有 姓张的信息
    List<String> list1 = new ArrayList<>();
    for (String s : list) {
        if (s.startsWith("张")) {
            list1.add(s);
        }
    }
    // 2.获取名称长度为3的用户
    List<String> list2 = new ArrayList<>();
    for (String s : list1) {
        if (s.length() == 3) {
            list2.add(s);
        }
    }
    // 3. 输出所有的用户信息
    for (String s : list2) {
        System.out.println(s);
    }
}

上面的代码针对与我们不同的需求总是一次次的循环循环循环.这时我们希望有更加高效的处理方式,这时我们就可以通过JDK8中提供的Stream API来解决这个问题了。

public static void main(String[] args) {
    // 定义一个List集合
    List<String> list = Arrays.asList("张三", "张三丰", "成龙", "周星驰");
    list.stream()
            // 1.获取所有 姓张的信息
            .filter(s -> s.startsWith("张"))
            // 2.获取名称长度为3的用户
            .filter(s -> s.length() == 3)
            // 3. 输出所有的用户信息
            .forEach(System.out::println);
}

二、Steam流式思想概述

StreamIO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

JDK8新特性_第1张图片

Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约。

三、Stream流的获取方式

①、根据Collection获取

java.util.Collection 接口中加入了default方法 stream,也就是说Collection接口下的所有的实 现都可以通过stream方法来获取Stream流。

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.stream();
    Set<String> set = new HashSet<>();
    set.stream();
    Vector vector = new Vector();
    vector.stream();
}

由于Map接口别没有实现Collection接口,可以根据Map获取对应的key value的集合。

public static void main(String[] args) {
    Map<String,Object> map = new HashMap<>();
    Stream<String> stream = map.keySet().stream(); // key
    Stream<Object> stream1 = map.values().stream(); // value
    Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); //entry
}

②、Stream.of方法

在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所有Stream接口中提供了静态方法of基本数据类型的数组是不行的

public static void main(String[] args) {
    Stream<String> a1 = Stream.of("a1", "a2", "a3");
    String[] arr1 = {"aa","bb","cc"};
    Stream<String> arr11 = Stream.of(arr1);
    Integer[] arr2 = {1,2,3,4};
    Stream<Integer> arr21 = Stream.of(arr2);
    arr21.forEach(System.out::println);
}

四、Stream常用方法

方法名 方法作用 返回值类型 方法种类
count 统计个数 long 终结
forEach 逐一处理 void 终结
filter 过滤 Stream 函数拼接
limit 取用前几个 Stream 函数拼接
skip 跳过前几个 Stream 函数拼接
map 映射 Stream 函数拼接
concat 组合 Stream 函数拼接

终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 countforEach 方法。

非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结方法。)

注意事项

  • Stream只能操作一次
  • Stream方法返回的是新的流
  • Stream不调用终结方法,中间的操作不会执行

①、forEach

forEach用来遍历流中的数据的

public static void main(String[] args) {
    Stream.of("1", "2", "3").forEach(System.out::println);
}

该方法接受一个Consumer接口(一个输入,没有返回),会将每一个流元素交给函数处理

void forEach(Consumer<? super T> action)

②、count

Stream流中的count方法用来统计其中的元素个数的

public static void main(String[] args) {
    System.out.println(Stream.of("1", "2", "3").count());
}

该方法返回一个long值,代表元素的个数

long count();

③、filter

filter方法的作用是用来过滤数据的,返回符合条件的数据,也可以通过filter方法将一个流转换成另一个子集流

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3", "bb", "cc", "aa", "dd")
        .filter((s) -> s.contains("a"))
        .forEach(System.out::println);
}

该接口接收一个Predicate函数式接口(一个输入,返回boolean)参数作为过滤条件

Stream<T> filter(Predicate<? super T> predicate);

④、limit

limit方法可以对流进行截取处理,只取前n个数据

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3", "bb", "cc", "aa", "dd")
            .limit(3)
            .forEach(System.out::println);
}

参数是一个long类型的数值,如果集合当前长度大于参数就进行截取,否则不操作

⑤、skip

如果希望跳过前面几个元素,可以使用skip方法获取一个截取之后的新流

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3", "bb", "cc", "aa", "dd")
            .skip(3)
            .forEach(System.out::println);
}

⑥、map

如果我们需要将流中的元素映射到另一个流中,可以使用map方法

public static void main(String[] args) {
    Stream.of("1", "2", "3", "4", "5", "6", "7")
        .map(Integer::parseInt)
        .forEach(System.out::println);
}

该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的数据

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

⑦、sorted

如果需要将数据排序,可以使用sorted方法

public static void main(String[] args) {
    Stream.of("1", "3", "2", "4", "0", "9", "7")
            .map(Integer::parseInt)
            // 根据数据的自然顺序排序
            //.sorted()
            // 根据比较强指定排序规则
            .sorted((o1, o2) -> o2 - o1)
            .forEach(System.out::println);
}

⑧、distinct

如果要去掉重复数据,可以使用distinct方法

public static void main(String[] args) {
    Stream.of(
            new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 18)
    ).distinct()
            .forEach(System.out::println);
}

Stream流中的distinct方法对于基本数据类型是可以直接出重的,但是对于自定义类型,我们是需要重写hashCodeequals方法来移除重复元素。

⑨、match

如果需要判断数据是否匹配指定的条件,可以使用match相关的方法

public static void main(String[] args) {
    boolean b = Stream.of("1", "3", "3", "4", "5", "1", "7")
        .map(Integer::parseInt)
        // 元素是否都满足条件
        //.allMatch(s -> s > 0)
        // 元素是否有任意一个满足条件
        //.anyMatch(s -> s >4)
        // 元素是否都不满足条件
        .noneMatch(s -> s > 4);
    System.out.println(b);
}

⑩、reduce

如果需要将所有数据归纳得到一个数据,可以使用reduce方法

public static void main(String[] args) {
    Integer sum = Stream.of(4, 5, 3, 9)
            // 第一个参数identity是默认值,第一次的时候会将默认值赋值给x,之后每次会将上一次的操作结果赋值给x y就是每次从数据中获取的元素
            .reduce(0, (x, y) -> {
                System.out.println("x=" + x + ",y=" + y);
                return x + y;
            });
    System.out.println(sum);
    // 获取 最大值
    Integer max = Stream.of(4, 5, 3, 9)
            .reduce(0, (x, y) -> x > y ? x : y);
    System.out.println(max);
}

⑪、concat

如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat

public static void main(String[] args) {
    Stream<String> stream1 = Stream.of("a", "b", "c");
    Stream<String> stream2 = Stream.of("x", "y", "z");
    // 通过concat方法将两个流合并为一个新的流
    Stream.concat(stream1, stream2).forEach(System.out::println);
}

⑫、find

如果我们需要找到某些数据,可以使用find方法来实现 ,返回的Optional对象,关于这个对象,后面会提到

Optional<T> findFirst();
Optional<T> findAny();

通过名字,就可以看到,对这个集合的流,做一系列的中间操作后,可以调用findFirst,返回集合的第一个对象,findAny返回这个集合中,取到的任何一个对象

  • 在串行的流中,findAnyfindFirst返回的,都是第一个对象
  • 而在并行的流中,findAny返回的是最快处理完的那个线程的数据,所以说,在并行操作中,对数据没有顺序上的要求,那么findAny的效率会比findFirst要快的
public static void main(String[] args) {
    Optional<String> first = Stream.of("1", "3", "3", "4", "5", "1",
            "7").findFirst();
    System.out.println(first.get());
    Optional<String> any = Stream.of("1", "3", "3", "4", "5", "1",
            "7").findAny();
    System.out.println(any.get());
}

⑬、 max和min

如果我们想要获取最大值和最小值,那么可以使用maxmin方法,返回的对象同样是Optional

Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);

使用

public static void main(String[] args) {
    Optional<Integer> max = Stream.of("1", "3", "3", "4", "5", "1", "7")
            .map(Integer::parseInt)
            .max(Comparator.comparingInt(o -> o));
    System.out.println(max.get());
    Optional<Integer> min = Stream.of("1", "3", "3", "4", "5", "1", "7")
            .map(Integer::parseInt)
            .min(Comparator.comparingInt(o -> o));
    System.out.println(min.get());
}

⑭、mapToInt

如果需要将Stream中的Integer类型转换成int类型,可以使用mapToInt方法来实现

JDK8新特性_第2张图片

public static void main(String[] args) {
    // Integer占用的内存比int多很多,在Stream流操作中会自动装修和拆箱操作
    Integer[] arr = {1, 2, 3, 5, 6, 8};
    Stream.of(arr)
            .filter(i -> i > 0)
            .forEach(System.out::println);
    System.out.println("---------");
    // 为了提高程序代码的效率,我们可以先将流中Integer数据转换为int数据,然后再操作
    IntStream intStream = Stream.of(arr)
            .mapToInt(Integer::intValue);
    intStream.filter(i -> i > 3)
            .forEach(System.out::println);
}

⑮、map和reduce的组合

在实际开发中我们经常会将mapreduce一块来使用

public static void main(String[] args) {
    // 1.求出所有年龄的总和
    Integer sumAge = Stream.of(new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 13)
            , new Person("王五", 15)
            , new Person("张三", 19))
            // 实现数据类型的转换
            .map(Person::getAge)
            .reduce(0, Integer::sum);
    System.out.println(sumAge);
    // 2.求出所有年龄中的最大值
    Integer maxAge = Stream.of(new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 13)
            , new Person("王五", 15)
            , new Person("张三", 19))
            // 实现数据类型的转换,符合reduce对数据的要求
            .map(Person::getAge)
            // reduce实现数据的处理
            .reduce(0, Math::max);
    System.out.println(maxAge);
    // 3.统计 字符 a 出现的次数
    Integer count = Stream.of("a", "b", "c", "d", "a", "c", "a")
            .map(ch -> "a".equals(ch) ? 1 : 0)
            .reduce(0, Integer::sum);
    System.out.println(count);
}

⑯、综合案例

定义两个集合,然后在集合中存储多个用户名称。然后完成如下的操作:

  • 第一个队伍只保留姓名长度为3的成员
  • 第一个队伍筛选之后只要前3个人
  • 第二个队伍只要姓张的成员
  • 第二个队伍筛选之后不要前两个人
  • 将两个队伍合并为一个队伍
  • 根据姓名创建Person对象
  • 打印整个队伍的Person信息
/**
 * 1. 第一个队伍只保留姓名长度为3的成员
 * 2. 第一个队伍筛选之后只要前3个人
 * 3. 第二个队伍只要姓张的成员
 * 4. 第二个队伍筛选之后不要前两个人
 * 5. 将两个队伍合并为一个队伍
 * 6. 根据姓名创建Person对象
 * 7. 打印整个队伍的Person信息
 */
public static void main(String[] args) {
    List<String> list1 = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子",
                                       "庄子", "孙子", "洪七 公");
    List<String> list2 = Arrays.asList("古力娜扎", "张无忌", "张三丰", "赵丽颖",
                                       "张二狗", "张天爱", "张三");
    // 1. 第一个队伍只保留姓名长度为3的成员
    // 2. 第一个队伍筛选之后只要前3个人
    Stream<String> stream1 = list1.stream().filter(s -> s.length() ==
                                                   3).limit(3);
    // 3. 第二个队伍只要姓张的成员
    // 4. 第二个队伍筛选之后不要前两个人
    Stream<String> stream2 = list2.stream().filter(s ->
                                                   s.startsWith("张")).skip(2);
    // 5. 将两个队伍合并为一个队伍
    // 6. 根据姓名创建Person对象
    // 7. 打印整个队伍的Person信息
    Stream.concat(stream1, stream2)
        .map(Person::new)
        .forEach(System.out::println);
}

五、Stream结果收集

①、结果收集到集合中

public static void main(String[] args) {
    List<String> list = Stream.of("aa", "bb", "cc", "aa")
        .collect(Collectors.toList());
    System.out.println(list);
    // 收集到 Set集合中
    Set<String> set = Stream.of("aa", "bb", "cc", "aa")
        .collect(Collectors.toSet());
    System.out.println(set);
    // 如果需要获取的类型为具体的实现,比如:ArrayList HashSet
    ArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa")
        .collect(Collectors.toCollection(ArrayList<String>::new));
    System.out.println(arrayList);
    HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa")
        .collect(Collectors.toCollection(HashSet<String>::new));
    System.out.println(hashSet);
}

②、结果收集到数组中

Stream中提供了toArray方法来将结果放到一个数组中,返回值类型是Object[],如果我们要指定返回的类型,那么可以使用另一个重载的toArray(IntFunction f)方法

public static void main(String[] args) {
    Object[] objects = Stream.of("aa", "bb", "cc", "aa")
            // 返回的数组中的元素是 Object类型
            .toArray();
    System.out.println(Arrays.toString(objects));
    // 如果我们需要指定返回的数组中的元素类型
    String[] strings = Stream.of("aa", "bb", "cc", "aa")
            .toArray(String[]::new);
    System.out.println(Arrays.toString(strings));
}

③、对流中的数据做聚合计算

我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作,比如获得最大值,最小值,求和,平均值,统计数量。

public static void main(String[] args) {
    // 获取年龄的最大值
    Optional<Person> maxAge = Stream.of(
            new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 13)
            , new Person("王五", 15)
            , new Person("张三", 19)
    ).max(Comparator.comparingInt(Person::getAge));
    System.out.println("最大年龄:" + maxAge.get());
    // 获取年龄的最小值
    Optional<Person> minAge = Stream.of(
            new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 13)
            , new Person("王五", 15)
            , new Person("张三", 19)
    ).min(Comparator.comparingInt(Person::getAge));
    System.out.println("最新年龄:" + minAge.get());
    // 求所有人的年龄之和
    int sumAge = Stream.of(
            new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 13)
            , new Person("王五", 15)
            , new Person("张三", 19)
    ).mapToInt(Person::getAge).sum();
    System.out.println("年龄总和:" + sumAge);
    // 年龄的平均值
    Double avgAge = Stream.of(
            new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 13)
            , new Person("王五", 15)
            , new Person("张三", 19)
    ).collect(Collectors.averagingInt(Person::getAge));
    System.out.println("年龄的平均值:" + avgAge);
    // 统计数量
    long count = Stream.of(
            new Person("张三", 18)
            , new Person("李四", 22)
            , new Person("张三", 13)
            , new Person("王五", 15)
            , new Person("张三", 19)
    ).filter(p -> p.getAge() > 18).count();
    System.out.println("满足条件的记录数:" + count);
}

④、对流中数据做分组操作

public static void main(String[] args) {
    // 根据账号对数据进行分组
    Map<String, List<Person>> map1 = Stream.of(
            new Person("张三", 18, 175)
            , new Person("李四", 22, 177)
            , new Person("张三", 14, 165)
            , new Person("李四", 15, 166)
            , new Person("张三", 19, 182)
    ).collect(Collectors.groupingBy(Person::getName));
    map1.forEach((k, v) -> System.out.println("k=" + k + "\t" + "v=" + v));
    System.out.println("-----------");
    // 根据年龄分组 如果大于等于18 成年否则未成年
    Map<String, List<Person>> map2 = Stream.of(
            new Person("张三", 18, 175)
            , new Person("李四", 22, 177)
            , new Person("张三", 14, 165)
            , new Person("李四", 15, 166)
            , new Person("张三", 19, 182)
    ).collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));
    map2.forEach((k, v) -> System.out.println("k=" + k + "\t" + "v=" + v));
}

多级分组: 先根据name分组然后根据年龄分组

public static void main(String[] args) {
    // 先根据name分组,然后根据age(成年和未成年)分组
    Map<String, Map<Object, List<Person>>> map = Stream.of(
            new Person("张三", 18, 175)
            , new Person("李四", 22, 177)
            , new Person("张三", 14, 165)
            , new Person("李四", 15, 166)
            , new Person("张三", 19, 182)
    ).collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年")));
    map.forEach((k, v) -> {
        System.out.println(k);
        v.forEach((k1, v1) -> {
            System.out.println("\t" + k1 + "=" + v1);
        });
    });
}

⑤、对流中的数据做分区操作

Collectors.partitioningBy会根据值是否为true,把集合中的数据分割为两个列表,一个true列表,一个 false列表

public static void main(String[] args) {
    Map<Boolean, List<Person>> map = Stream.of(
            new Person("张三", 18, 175)
            , new Person("李四", 22, 177)
            , new Person("张三", 14, 165)
            , new Person("李四", 15, 166)
            , new Person("张三", 19, 182)
    ).collect(Collectors.partitioningBy(p -> p.getAge() > 18));
    map.forEach((k, v) -> System.out.println(k + "\t" + v));
}

⑥、对流中的数据做拼接

Collectors.joining会根据指定的连接符,将所有的元素连接成一个字符串

public static void main(String[] args) {
    String s1 = Stream.of(
            new Person("张三", 18, 175)
            , new Person("李四", 22, 177)
            , new Person("张三", 14, 165)
            , new Person("李四", 15, 166)
            , new Person("张三", 19, 182)
    ).map(Person::getName).collect(Collectors.joining());
    // 张三李四张三李四张三
    System.out.println(s1);
    String s2 = Stream.of(
            new Person("张三", 18, 175)
            , new Person("李四", 22, 177)
            , new Person("张三", 14, 165)
            , new Person("李四", 15, 166)
            , new Person("张三", 19, 182)
    ).map(Person::getName).collect(Collectors.joining("_"));
    // 张三_李四_张三_李四_张三
    System.out.println(s2);
    String s3 = Stream.of(
            new Person("张三", 18, 175)
            , new Person("李四", 22, 177)
            , new Person("张三", 14, 165)
            , new Person("李四", 15, 166)
            , new Person("张三", 19, 182)
    ).map(Person::getName).collect(Collectors.joining("_", "###", "$$$"));
    // ###张三_李四_张三_李四_张三$$$
    System.out.println(s3);
}

六、并行的Stream流

①、串行的Stream流

介绍并行Stream流之前先介绍一下串行的Stream流,我们前面使用的Stream流都是串行,也就是在一个线程上面执行

public static void main(String[] args) {
    System.out.println(Stream.of(5, 6, 8, 3, 1, 6)
            .filter(s -> {
                System.out.println(Thread.currentThread() + "" + s);
                return s > 3;
            }).count());
}

②、并行流

parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速 度。

Ⅰ、获取并行流

我们可以通过两种方式来获取并行流

  • 通过集合接口中的parallelStream方法来获取
  • 通过已有的串行流转换为并行流(parallel
Ⅱ、并行流操作
public static void main(String[] args) {
    System.out.println(Stream.of(1, 4, 2, 6, 1, 5, 9)
                       // 将流转换为并发流,Stream处理的时候就会通过多线程处理
                       .parallel()
                       .filter(s -> {
                           System.out.println(Thread.currentThread() + " s=" + s);
                           return s > 2;
                       }).count());
}
Ⅲ、并行流和串行流对比

Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是 一个线程操作。

Ⅳ、线程安全问题
public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
        list.add(i);
    }
    System.out.println(list.size());
    List<Integer> listNew = new ArrayList<>();
    // 使用并行流来向集合中添加数据
    list.parallelStream()
            .forEach(listNew::add);
    System.out.println(listNew.size());
}

观察上述代码,可能会导致listNew长度小于1000或直接报ArrayIndexOutOfBoundsException,解决办法有三种:

  • 加同步锁
public static void main(String[] args) {
    List<Integer> listNew = new ArrayList<>();
    Object obj = new Object();
    IntStream.rangeClosed(1, 1000)
            .parallel()
            .forEach(i -> {
                synchronized (obj) {
                    listNew.add(i);
                }
            });
    System.out.println(listNew.size());
}
  • 使用线程安全的容器
public static void main(String[] args) {
    List<Integer> listNew = new ArrayList<>();
    Object obj = new Object();
    IntStream.rangeClosed(1, 1000)
            .parallel()
            .forEach(i -> {
                synchronized (obj) {
                    listNew.add(i);
                }
            });
    System.out.println(listNew.size());
}
  • 使用Stream.toArray或者collect方法操作
public static void main(String[] args) {
    List<Integer> listNew = new ArrayList<>();
    Object obj = new Object();
    List<Integer> list = IntStream.rangeClosed(1, 1000)
            .parallel()
            .boxed()
            .collect(Collectors.toList());
    System.out.println(list.size());
}

Optional

一、什么是Optional

Optional是一个没有子类的工具类,Optional是一个可以为null的容器对象,它的主要作用就是为了避 免Null检查,防止NullpointerException

二、创建方式

①、of

此方法不支持传入null

Optional<String> op1 = Optional.of("zhangsan");

②、ofNullable

此方法支持传入null

Optional<String> op3 = Optional.ofNullable("lisi");
Optional<Object> op4 = Optional.ofNullable(null);

③、empty

直接创建一个空的Optional对象

Optional op5 = Optional.empty();

三、常用方法

①、get

如果Optional有值则返回,否则抛出NoSuchElementException异常get()通常和isPresent()方法一块使用

public static void main(String[] args) {
    // create a Optional
    Optional<Integer> op = Optional.of(9455);
    // print value
    System.out.println("Optional: " + op);
    // get the value
    System.out.println("Value of this Optional: " + op.get());
}

②、isPresent()

判断是否包含值,包含值返回true,不包含值返回false

public static void main(String[] args) {
    Optional<String> name = Optional.of("Dolores");
    Optional<String> empty = Optional.empty();
    //如果值不为null,orElse方法返回Optional实例的值。
    //如果为null,返回传入的消息。
    //输出:Dolores
    System.out.println(name.orElse("There is some value!"));
    //输出:There is no value present!
    System.out.println(empty.orElse("There is no value present!"));
}

③、orElse(T t)

如果调用对象包含值,就返回该值,否则返回t

public static void main(String[] args) {
    Person p = new Person("zhangsan", 18);
    Optional<Person> op = Optional.of(p);
    String name = getNameForOptional(op);
    System.out.println("name=" + name);
}

/**
 * 根据Person对象 将name转换为大写并返回
 * 通过Optional方式实现
 */
public static String getNameForOptional(Optional<Person> op) {
    if (op.isPresent()) {
        return op.map(Person::getName)
                .map(String::toUpperCase)
                .orElse("空值");
    }
    return null;
}

④、orElseGet(Supplier s)

如果调用对象包含值,就返回该值,否则返回Lambda表达式的返回值

public static void main(String[] args) {
    Optional<String> name = Optional.of("Dolores");
    Optional<String> empty = Optional.empty();
    //orElseGet与orElse方法类似,区别在于orElse传入的是默认值,
    //orElseGet可以接受一个lambda表达式生成默认值。
    //输出:Dolores
    System.out.println(name.orElseGet(() -> "it's value"));
    //输出:No value
    System.out.println(empty.orElseGet(() -> "No value"));
}

Lambda表达式方法引用

一、为什么要用方法引用

在使用Lambda表达式的时候,也会出现代码冗余的情况,比如:用Lambda表达式求一个数组的和

public static void main(String[] args) {
    printSum(a -> {
        // Lambda表达式中的代码和 getTotal中的代码冗余了
        int sum = 0;
        for (int i : a) {
            sum += i;
        }
        System.out.println("数组之和:" + sum);
    });
}

public static void getTotal(int[] a) {
    int sum = 0;
    for (int i : a) {
        sum += i;
    }
    System.out.println("数组之和:" + sum);
}

private static void printSum(Consumer<int[]> consumer) {
    int[] a = {10, 20, 30, 40, 50, 60};
    consumer.accept(a);
}

因为在Lambda表达式中要执行的代码和我们另一个方法中的代码是一样的,这时就没有必要重写一份逻辑了,这时我们就可以“引用”重复代码

public static void main(String[] args) {
    printSum(a -> {
        printSum(Main::getTotal);
    });
}

public static void getTotal(int[] a) {
    int sum = 0;
    for (int i : a) {
        sum += i;
    }
    System.out.println("数组之和:" + sum);
}

private static void printSum(Consumer<int[]> consumer) {
    int[] a = {10, 20, 30, 40, 50, 60};
    consumer.accept(a);
}

二、方法引用的五种方式

①、对象名::方法名

这是最常见的一种用法。如果一个类中的已经存在了一个成员方法,则可以通过对象名引用成员方法

public static void main(String[] args) {
    Date now = new Date();
    Supplier<Long> supplier = () -> {
        return now.getTime();
    };
    System.out.println(supplier.get());
    // 然后我们通过 方法引用 的方式来处理
    Supplier<Long> supplier1 = now::getTime;
    System.out.println(supplier1.get());
}

②、类名::静态方法名

public static void main(String[] args) {
    Supplier<Long> supplier1 = ()->{
        return System.currentTimeMillis();
    };
    System.out.println(supplier1.get());
    // 通过 方法引用 来实现
    Supplier<Long> supplier2 = System::currentTimeMillis;
    System.out.println(supplier2.get());
}

③、类名::引用实例方法

Java面向对象中,类名只能调用静态方法,类名引用实例方法是用前提的,实际上是拿第一个参数作 为方法的调用者

public static void main(String[] args) {
    Function<String, Integer> function = (s) -> {
        return s.length();
    };
    System.out.println(function.apply("hello"));
    // 通过方法引用来实现
    Function<String, Integer> function1 = String::length;
    System.out.println(function1.apply("hahahaha"));
    BiFunction<String, Integer, String> function2 = String::substring;
    String msg = function2.apply("HelloWorld", 3);
    System.out.println(msg);
}

④、类名::构造器

Java面向对象中,类名只能调用静态方法,类名引用实例方法是用前提的,实际上是拿第一个参数作 为方法的调用者

public static void main(String[] args) {
    Supplier<Person> sup = ()->{return new Person();};
    System.out.println(sup.get());
    // 然后通过 方法引用来实现
    Supplier<Person> sup1 = Person::new;
    System.out.println(sup1.get());
    BiFunction<String,Integer,Person> function = Person::new;
    System.out.println(function.apply("张三",22));
}

⑤、数组::构造器

public static void main(String[] args) {
    Supplier<Person> sup = () -> {
        return new Person();
    };
    System.out.println(sup.get());
    // 然后通过 方法引用来实现
    Supplier<Person> sup1 = Person::new;
    System.out.println(sup1.get());
    BiFunction<String, Integer, Person> function = Person::new;
    System.out.println(function.apply("张三", 22));
}

你可能感兴趣的:(java,数据结构,stream)