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

1.方法引用

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

1.1 使用场景

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

例:

 private static void test38() {
        getAuthors().stream()
                .map(author -> author.getName())
                .forEach(authorName -> System.out.println(authorName));
    }

对应的方法引用。

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

(1)引用类的静态方法

基本格式 类名::方法名

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

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

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

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

例:

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

其对应的方法引用。

  private static void test39() {
        StringBuilder sb = new StringBuilder();
        getAuthors().stream()
                .map(Author::getName)
                .forEach(sb::append);
        System.out.println(sb);
    }

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

基本格式 类名::方法名

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

例:

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

其方法引用为。

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)构造器引用

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

例:

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

方法引用对应如下。

  private static void test40() {
        getAuthors().stream()
                .map(Author::getName)
                .map(StringBuilder::new)
                .map(sb -> sb.append("---"))
                .forEach(System.out::println);
    }

2.Stream流的高级用法

2.1 基本数据类型的优化

参考如下代码。

 private static void test41() {
        getAuthors().stream()
                .map(author -> author.getAge() + 10)
                .filter(age -> age > 28)
                .forEach(System.out::println);
    }

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

 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…可以把流中的数据类型转换为基本数据类型,对上面的例子优化如下.

   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即可实现并行流。

例:

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

并行流的方式如下。

 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方法帮助我们进行调试,它不会像终结方法一样将流废弃。

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

输出如下。

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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半旧518

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值