java8系列05——方法引用与流的高级用法

2022-10-26 17:41:02 浏览数 (2)

目录

  • 1.方法引用
    • 1.1 使用场景
    • 1.2 语法详解(了解)

  • 2.Stream流的高级用法
    • 2.1 基本数据类型的优化
    • 2.2 并行流
    • 3.3 调试

1.方法引用

方法引用也是一个语法糖,可以进一步简化Lambda表达式。并不是所有的Lambda表达式和匿名内部类都可以转换为方法引用。

1.1 使用场景

在一个匿名内部类中,如果方法体中仅仅是一个方法的调用,或者是一个构造方法,那么它很可能就可以改造成为方法引用。

例:

代码语言:javascript复制
 private static void test38() {
        getAuthors().stream()
                .map(author -> author.getName())
                .forEach(authorName -> System.out.println(authorName));
    }

对应的方法引用。

代码语言:javascript复制
  private static void test38() {
        getAuthors().stream()
                .map(Author::getName)
                .forEach(System.out::println);
    }
1.2 语法详解(了解)

(1)引用类的静态方法

基本格式 类名::方法名

在一个匿名内部类中,如果方法体重写的方法中仅仅是一个某个类的静态方法的调用,并且,将要重写的抽象方法中所有参数都按照顺序传入到这个方法中。比如上例中的println方法。

(2)引用对象的实例方法

基本格式 对象名::方法名

在一个匿名内部类中,如果方法体重写的方法中仅仅是一个某个对象的成员方法的调用,并且,将要重写的抽象方法中所有参数都按照顺序传入到这个方法中。

例:

代码语言:javascript复制
private static void test39() {
        StringBuilder sb = new StringBuilder();
        getAuthors().stream()
                .map(Author::getName)
                .forEach(new Consumer<String>() {
                    @Override
                    public void accept(String s) {
                        sb.append(s);
                    }
                });
        System.out.println(sb);
    }

其对应的方法引用。

代码语言:javascript复制
  private static void test39() {
        StringBuilder sb = new StringBuilder();
        getAuthors().stream()
                .map(Author::getName)
                .forEach(sb::append);
        System.out.println(sb);
    }

(3)引用类的实例方法。

基本格式 类名::方法名

如果我们在重写方法时,方法体中只有一行代码,并且这行代码调用了第一个参数的成员方法,并且我们把抽象方法中剩余的参数按照顺序传入这个成员方法中,这个时候就可以使用类的实例方法。

例:

代码语言:javascript复制
public class MethodDemo {
     interface UseString {
        String use(String str, int start, int length);
    }

    public static String subAuthorName(String str, UseString us) {
         int start = 0;
         int length = 1;
         return us.use(str, start, length);
    }

    public static void main(String[] args) {
        subAuthorName("半旧518", new UseString() {
            @Override
            public String use(String str, int start, int length) {
                return str.substring(start, length);
            }
        });
    }
}

其方法引用为。

代码语言:javascript复制
public class MethodDemo {
     interface UseString {
        String use(String str, int start, int length);
    }

    public static String subAuthorName(String str, UseString us) {
         int start = 0;
         int length = 1;
         return us.use(str, start, length);
    }

    public static void main(String[] args) {
        String subName = subAuthorName("半旧518", String::substring);
        System.out.println(subName);
    }
}

实际上,我们最开始的Author::getName就是第三中方法引用。

当然,记不住还是可以在Idea中用alt键和enter键来快速的实现转换。

(4)构造器引用

如果匿名内部类在重写方法时,方法体中只有一行代码,并且这行代码就是调用某个构造方法,就可以使用构造器引用。

例:

代码语言:javascript复制
  private static void test40() {
        getAuthors().stream()
                .map(author -> author.getName())
                .map(name -> new StringBuilder(name))
                .map(sb -> sb.append("---"))
                .forEach(System.out::println);
    }

方法引用对应如下。

代码语言:javascript复制
  private static void test40() {
        getAuthors().stream()
                .map(Author::getName)
                .map(StringBuilder::new)
                .map(sb -> sb.append("---"))
                .forEach(System.out::println);
    }

2.Stream流的高级用法

2.1 基本数据类型的优化

参考如下代码。

代码语言:javascript复制
 private static void test41() {
        getAuthors().stream()
                .map(author -> author.getAge()   10)
                .filter(age -> age > 28)
                .forEach(System.out::println);
    }

看上去好像没有什么问题。转换为匿名内部类看看。

代码语言:javascript复制
 private static void test41() {
        getAuthors().stream()
                .map(new Function<Author, Integer>() {
                    @Override
                    public Integer apply(Author author) {
                        return author.getAge()   10;
                    }
                })
                .filter(new Predicate<Integer>() {
                    @Override
                    public boolean test(Integer age) {
                        return age > 18;
                    }
                })
                .forEach(System.out::println);
    }

applytest中参数类型都包含基本数据类型Integer,在进行运算时,会先自动拆箱,再自动装箱,如果操作的数据元素特别多,这会造成不小的时间损耗.

java8对于基本数据类型的操作提供优化的方法:mapToInt,mapToLong…可以把流中的数据类型转换为基本数据类型,对上面的例子优化如下.

代码语言:javascript复制
   private static void test41() {
        getAuthors().stream()
                .mapToInt(Author::getAge)
                .map(age -> age   10)
                .filter(age -> age > 18)
                .forEach(System.out::println);
    }
2.2 并行流

我们之前操作的流都是以串行的方式完成,对于大数据量的情况,串行的方式时间损耗会较大.java8提供了并行流,将数据的处理分配到多个线程进行处理.而且这种方式比自己实现多线程更加的轻量级,也不要考虑头疼的线程安全问题。使用parallel即可实现并行流。

例:

代码语言:javascript复制
    private static void test42() {
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
        Integer intNum = integerStream.filter(num -> num > 5)
                .reduce(new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer result, Integer num) {
                        return result   num;
                    }
                }).get();
        System.out.println(intNum);
    }

并行流的方式如下。

代码语言:javascript复制
 private static void test42() {
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer intNum = integerStream
                .parallel()
                .filter(num -> num > 5)
                .reduce(new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer result, Integer num) {
                        return result   num;
                    }
                }).get();
        System.out.println(intNum);
    }

并行流的机制其实类似与流水线,比如前5个元素在线程1中完成过滤,后5个线程会在第2个线程中完成过滤.

3.3 调试

我们可以使用peek方法帮助我们进行调试,它不会像终结方法一样将流废弃。

代码语言:javascript复制
    private static void test42() {
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
        Integer intNum = integerStream
                .parallel()
                .peek(integer -> System.out.println(integer   " in "   Thread.currentThread()))
                .filter(num -> num > 5)
                .reduce((result, num) -> result   num).get();
        System.out.println(intNum);
    }

输出如下。

代码语言:javascript复制
9 in Thread[ForkJoinPool.commonPool-worker-13,5,main]
4 in Thread[ForkJoinPool.commonPool-worker-15,5,main]
2 in Thread[ForkJoinPool.commonPool-worker-11,5,main]
8 in Thread[ForkJoinPool.commonPool-worker-2,5,main]
7 in Thread[ForkJoinPool.commonPool-worker-4,5,main]
6 in Thread[main,5,main]
1 in Thread[ForkJoinPool.commonPool-worker-6,5,main]
3 in Thread[ForkJoinPool.commonPool-worker-9,5,main]
5 in Thread[ForkJoinPool.commonPool-worker-8,5,main]
30

0 人点赞