2022.11.11 方法引用与lambda并行流
6. 方法引用
我们在使用lambda时,如果方法体中只有一个方法的调用的话(包括构造方法),我们可以用方法引用进一步简化代码。
6.1 推荐用法
我们在使用lambda时不需要考虑什么时候用方法引用,用哪种方法引用,方法引用的格式是什么。我们只需要在写完lambda方法发现方法体只有一行代码,并且是方法的调用(a.get())时使用快捷键尝试是否能够转换成方法引用即可。
当我们方法引用使用的多了慢慢的也可以直接写出方法引用。
6.2 基本格式
类名或者对象名::方法名
6.3 语法详解(了解)
6.3.1 引用类的静态方法
其实就是引用类的静态方法
格式
类名::方法名
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。
例如:
如下代码就可以用方法引用进行简化
1 List<Author> authors = getAuthors(); 2 Stream<Author> authorStream = authors.stream(); 3 authorStream.map(author -> author.getAge()) 4 .map(new Function<Integer, String>() { 5 @Override 6 //参数age 传入了静态方法String.valueOf()中 7 public String apply(Integer age) { 8 //一行代码,调用类的静态方法 9 return String.valueOf(age); 10 } 11 }); 12 13 //将map转化为方法引用 14 List<Author> authors = getAuthors(); 15 Stream<Author> authorStream = authors.stream(); 16 authorStream.map(author -> author.getAge()) 17 .map(String::valueOf);//类名::方法名
注意,如果我们所重写的方法是没有参数的,调用的方法也是没有参数的也相当于符合以上规则。
优化后如下:
1 List<Author> authors = getAuthors(); 2 3 Stream<Author> authorStream = authors.stream(); 4 5 authorStream.map(author -> author.getAge()) 6 .map(String::valueOf);//类名::方法名
6.3.2 引用对象的实例方法
格式
对象名::方法名
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法
例如:
1 List<Author> authors = getAuthors(); 2 3 Stream<Author> authorStream = authors.stream(); 4 StringBuffer sb = new StringBuffer(); 5 authorStream.map(author -> author.getAge()) 6 .forEach(new Consumer<Integer>() { 7 @Override 8 public void accept(Integer name) { 9 // 调用对象的成员方法并且参数都传递了 10 sb.append(name); 11 } 12 });
优化后:
1 List<Author> authors = getAuthors(); 2 3 Stream<Author> authorStream = authors.stream(); 4 StringBuilder sb = new StringBuilder(); 5 authorStream.map(author -> author.getName()) 6 .forEach(sb::append);//对象名::方法名
6.3.4 引用类的实例方法
格式
类名::方法名
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法。
例如:
1 interface UseString{ 2 String use(String str,int start,int length); 3 } 4 5 public static String subAuthorName(String str, UseString useString){ 6 int start = 0; 7 int length = 1; 8 return useString.use(str,start,length); 9 } 10 public static void main(String[] args) { 11 12 subAuthorName("三更草堂", new UseString() { 13 @Override 14 public String use(String str, int start, int length) { 15 //调用了第一个参数的成员方法 剩余的参数都传入了方法中 16 return str.substring(start,length); 17 } 18 }); 19 20 }
优化后如下:
1 interface UseString{ 2 String use(String str,int start,int length); 3 } 4 5 public static String subAuthorName(String str, UseString useString){ 6 int start = 0; 7 int length = 1; 8 return useString.use(str,start,length); 9 } 10 public static void main(String[] args) { 11 subAuthorName("三更草堂", String::substring);//类名::成员方法 12 }
6.3.5 构造器引用
如果方法体中的一行代码是构造器的话就可以使用构造器引用。
格式
类名::new
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器。
例如:
1 List<Author> authors = getAuthors(); 2 authors.stream() 3 .map(author -> author.getName()) 4 .map(new Function<String, StringBuilder>() { 5 @Override 6 public StringBuilder apply(String name) { 7 //调用构造器 把apply方法的参数都传入了构造器 8 return new StringBuilder(name); 9 } 10 }) 11 .map(sb->sb.append("-三更").toString()) 12 .forEach(str-> System.out.println(str));
优化后:
1 List<Author> authors = getAuthors(); 2 authors.stream() 3 .map(author -> author.getName()) 4 .map(StringBuilder::new) 5 .map(sb->sb.append("-三更").toString()) 6 .forEach(str-> System.out.println(str));
7. 高级用法
基本数据类型优化
我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。
即使我们操作的是整数小数,但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候,你就不能无视这个时间损耗了。
所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。
例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。
1 private static void test27() { 2 3 List<Author> authors = getAuthors(); 4 authors.stream() 5 .map(author -> author.getAge())//返回值为Integer 6 .map(age -> age + 10) 7 .filter(age->age>18) 8 .map(age->age+2) 9 .forEach(System.out::println); 10 11 authors.stream() 12 .mapToInt(author -> author.getAge())//返回值为int 省去了自动装箱拆箱的时间 13 .map(age -> age + 10) 14 .filter(age->age>18) 15 .map(age->age+2) 16 .forEach(System.out::println); 17 }
并行流
当流中有大量元素时,我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完全。如果我们自己去用代码实现的话其实会非常的复杂,并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话,我们只需要修改一个方法的调用就可以使用并行流来帮我们实现,从而提高效率。
parallel方法可以把串行流转换成并行流。
1 private static void test28() { 2 Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 3 Integer sum = stream.parallel() //变为了并行流,后面的操作多线程完成 4 .peek(new Consumer<Integer>() { //中间操作可以打印调试数据 forEach为终结操作 5 @Override 6 public void accept(Integer num) { 7 System.out.println(num+Thread.currentThread().getName()); 8 } 9 }) 10 .filter(num -> num > 5) 11 .reduce((result, ele) -> result + ele) 12 .get(); 13 System.out.println(sum); 14 }
也可以通过parallelStream直接获取并行流对象。
1 List<Author> authors = getAuthors(); 2 authors.parallelStream()//直接转化为并行流 3 .map(author -> author.getAge()) 4 .map(age -> age + 10) 5 .filter(age->age>18) 6 .map(age->age+2) 7 .forEach(System.out::println);