目录
- 1. Streams简介
- 1.1 创建Stream
- 1.2 Streams多线程
- 1.3 Stream的基本操作
- Matching
- Filtering
- Mapping
- FlatMap
- Reduction
- Collecting
- 2. functional interface的分类和使用
- 2.1 Functional Interface
- 2.2 Function:一个参数一个返回值
- 2.3 BiFunction:接收两个参数,一个返回值
- 2.4 Supplier:无参的Function
- 2.5 Consumer:接收一个参数,不返回值
- 2.6 Predicate:接收一个参数,返回boolean
- 2.7 Operator:接收和返回同样的类型
- 3. Lambda表达式最佳实践
- 3.1 优先使用标准Functional接口
- 3.2 使用@FunctionalInterface注解
- 3.3 在Functional Interfaces中不要滥用Default Methods
- 3.4 使用Lambda 表达式来实例化Functional Interface
- 3.5 不要重写Functional Interface作为参数的方法
- 3.6 Lambda表达式和内部类是不同的
- 3.7 Lambda Expression尽可能简洁
- 3.8 使用方法引用
- 3.9 Effectively Final 变量
- 4. stream表达式中实现if/else逻辑
- 4.1 传统写法
- 4.2 使用filter
- 5. 在map中使用stream
- 5.1 基本概念
- 5.2 使用Stream获取map的key
- 5.3 使用stream获取map的value
- 6. Stream中的操作类型和peek的使用
- 6.1 中间操作和终止操作
- 6.2 peek
- 7. lambda表达式中的异常处理
- 7.1 处理Unchecked Exception
- 7.2 处理checked Exception
- 8. stream中throw Exception
- 8.1 throw小诀窍
- 9. stream中Collectors的用法
- 9.1 Collectors.toList()
- 9.2 Collectors.toSet()
- 9.3 Collectors.toCollection()
- 9.4 Collectors.toMap()
- 9.5 Collectors.collectingAndThen()
- 9.6 Collectors.joining()
- 9.7 Collectors.counting()
- 9.8 Collectors.summarizingDouble/Long/Int()
- 9.9 Collectors.averagingDouble/Long/Int()
- 9.10 Collectors.summingDouble/Long/Int()
- 9.11 Collectors.maxBy()/minBy()
- 9.12 Collectors.groupingBy()
- 9.13 Collectors.partitioningBy()
- 10. 创建一个自定义的collector
- 10.1 Collector介绍
- 10.2 自定义Collector
- 11. stream reduce详解和误区
- 11.1 reduce详解
- 12. stream中的Spliterator
- 12.1 tryAdvance
- 12.2 trySplit
- 12.3 estimateSize
- 12.4 characteristics
- 12.5 举个例子
- 13. break stream的foreach
- 13.1 使用Spliterator
- 13.2 自定义forEach方法
- 14. predicate chain的使用
- 14.1 基本使用
- 14.2 使用多个Filter
- 14.3 使用复合Predicate
- 14.4 组合Predicate
- 14.5 Predicate的集合操作
- 15. 中构建无限的stream
- 15.1 基本使用
- 15.2 自定义类型
- 16. 自定义parallelStream的thread pool
- 16.1 通常操作
- 16.2 使用自定义ForkJoinPool
- 17. 总结
1. Streams简介
今天要讲的Stream指的是java.util.stream包中的诸多类。Stream可以方便的将之前的结合类以转换为Stream并以流式方式进行处理,大大的简化了我们的编程,Stream包中,最核心的就是interface Stream
从上面的图中我们可以看到Stream继承自BaseStream。Stream中定义了很多非常实用的方法,比如filter,map,flatmap,forEach,reduce,collect等等。接下来我们将会逐一讲解。
1.1 创建Stream
Stream的创建有很多方式,java引入Stream之后所有的集合类都添加了一个stream()方法,通过这个方法可以直接得到其对应的Stream。也可以通过Stream.of方法来创建:
//Stream Creation String[] arr = new String[]{"a", "b", "c"}; Stream<String> stream = Arrays.stream(arr); stream = Stream.of("a", "b", "c");
1.2 Streams多线程
如果我们想使用多线程来处理集合类的数据,Stream提供了非常方便的多线程方法parallelStream():
//Multi-threading List<String> list =new ArrayList(); list.add("aaa"); list.add("bbb"); list.add("abc"); list.add("ccc"); list.add("ddd"); list.parallelStream().forEach(element -> doPrint(element));
1.3 Stream的基本操作
Stream的操作可以分为两类,一类是中间操作,中间操作返回Stream,因此可以级联调用。 另一类是终止操作,这类操作会返回Stream定义的类型。
//Operations long count = list.stream().distinct().count();
上面的例子中,distinct()返回一个Stream,所以可以级联操作,最后的count()是一个终止操作,返回最后的值。
Matching
Stream提供了anyMatch(), allMatch(), noneMatch()这三种match方式,我们看下怎么使用:
//Matching boolean isValid = list.stream().anyMatch(element -> element.contains("h")); boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h"));
Filtering
filter() 方法允许我们对Stream中的数据进行过滤,从而得到我们需要的:
Stream<String> filterStream = list.stream().filter(element -> element.contains("d"));
上面的例子中我们从list中选出了包含“d”字母的String。
Mapping
map就是对Stream中的值进行再加工,然后将加工过后的值作为新的Stream返回。
//Mapping Stream<String> mappingStream = list.stream().map(element -> convertElement(element)); private static String convertElement(String element) { return "element"+"abc"; }
上的例子中我们把list中的每个值都加上了“abc”然后返回一个新的Stream。
FlatMap
flatMap和Map很类似,但是他们两个又有不同,看名字我们可以看到flatMap意思是打平之后再做Map。
怎么理解呢?
假如我们有一个CustBook类:
@Data public class CustBook { List<String> bookName; }
CustBook定义了一个bookName字段。
先看一下Map返回的结果:
List<CustBook> users = new ArrayList<>(); users.add(new CustBook()); Stream<Stream<String>> userStreamMap = users.stream().map(user -> user.getBookName().stream());
在上面的代码中,map将每一个user都转换成了stream,所以最后的结果是返回Stream的Stream。
如果我们只想返回String,则可以使用FlatMap:
List<CustBook> users = new ArrayList<>(); users.add(new CustBook()); Stream<String> userStream = users.stream().map(user -> user.getBookName().stream());
简单点讲FlatMap就是将层级关系铺平重来。
Reduction
使用reduce() 方法可以方便的对集合的数据进行运算,reduce()接收两个参数,第一个是开始值,后面是一个函数表示累计。
//Reduction List<Integer> integers = Arrays.asList(1, 1, 1); Integer reduced = integers.stream().reduce(100, (a, b) -> a + b);
上面的例子我们定义了3个1的list,然后调用reduce(100, (a, b) -> a + b)方法,最后的结果是103.
Collecting
collect()方法可以方便的将Stream再次转换为集合类,方便处理和展示:
List<String> resultList = list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());
2. functional interface的分类和使用
java 8引入了lambda表达式,lambda表达式实际上表示的就是一个匿名的function。
在java 8之前,如果需要使用到匿名function需要new一个类的实现,但是有了lambda表达式之后,一切都变的非常简介。
我们看一个之前讲线程池的时候的一个例子:
//ExecutorService using class ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(new Runnable() { @Override public void run() { log.info("new runnable"); } });
executorService.submit需要接收一个Runnable类,上面的例子中我们new了一个Runnable类,并实现了它的run()方法。
上面的例子如果用lambda表达式来重写,则如下所示:
//ExecutorService using lambda executorService.submit(()->log.info("new runnable"));
看起是不是很简单,使用lambda表达式就可以省略匿名类的构造,并且可读性更强。
那么是不是所有的匿名类都可以用lambda表达式来重构呢?也不是。
我们看下Runnable类有什么特点:
@FunctionalInterface public interface Runnable
Runnable类上面有一个@FunctionalInterface注解。这个注解就是我们今天要讲到的Functional Interface。
2.1 Functional Interface
Functional Interface是指带有 @FunctionalInterface 注解的interface。它的特点是其中只有一个子类必须要实现的abstract方法。如果abstract方法前面带有default关键字,则不做计算。
其实这个也很好理解,因为Functional Interface改写成为lambda表达式之后,并没有指定实现的哪个方法,如果有多个方法需要实现的话,就会有问题。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
Functional Interface一般都在java.util.function包中。
根据要实现的方法参数和返回值的不同,Functional Interface可以分为很多种,下面我们分别来介绍。
2.2 Function:一个参数一个返回值
Function接口定义了一个方法,接收一个参数,返回一个参数。
@FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t);
一般我们在对集合类进行处理的时候,会用到Function。
Map<String, Integer> nameMap = new HashMap<>(); Integer value = nameMap.computeIfAbsent("name", s -> s.length());
上面的例子中我们调用了map的computeIfAbsent方法,传入一个Function。
上面的例子还可以改写成更短的:
Integer value1 = nameMap.computeIfAbsent("name", String::length);
Function没有指明参数和返回值的类型,如果需要传入特定的参数,则可以使用IntFunction, LongFunction, DoubleFunction:
@FunctionalInterface public interface IntFunction<R> { /** * Applies this function to the given argument. * * @param value the function argument * @return the function result */ R apply(int value); }
如果需要返回特定的参数,则可以使用ToIntFunction, ToLongFunction, ToDoubleFunction:
@FunctionalInterface public interface ToDoubleFunction<T> { /** * Applies this function to the given argument. * * @param value the function argument * @return the function result */ double applyAsDouble(T value); }
如果要同时指定参数和返回值,则可以使用DoubleToIntFunction, DoubleToLongFunction, IntToDoubleFunction, IntToLongFunction, LongToIntFunction, LongToDoubleFunction:
@FunctionalInterface public interface LongToIntFunction { /** * Applies this function to the given argument. * * @param value the function argument * @return the function result */ int applyAsInt(long value); }
2.3 BiFunction:接收两个参数,一个返回值
如果需要接受两个参数,一个返回值的话,可以使用BiFunction:BiFunction, ToDoubleBiFunction, ToIntBiFunction, ToLongBiFunction等。
@FunctionalInterface public interface BiFunction<T, U, R> { /** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u);
我们看一个BiFunction的例子:
//BiFunction Map<String, Integer> salaries = new HashMap<>(); salaries.put("alice", 100); salaries.put("jack", 200); salaries.put("mark", 300); salaries.replaceAll((name, oldValue) -> name.equals("alice") ? oldValue : oldValue + 200);
2.4 Supplier:无参的Function
如果什么参数都不需要,则可以使用Supplier:
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
2.5 Consumer:接收一个参数,不返回值
Consumer接收一个参数,但是不返回任何值,我们看下Consumer的定义:
@FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t);
看一个Consumer的具体应用:
//Consumer nameMap.forEach((name, age) -> System.out.println(name + " is " + age + " years old"));
2.6 Predicate:接收一个参数,返回boolean
Predicate接收一个参数,返回boolean值:
@FunctionalInterface public interface Predicate<T> { /** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t);
如果用在集合类的过滤上面那是极好的:
//Predicate List<String> names = Arrays.asList("A", "B", "C", "D", "E"); List<String> namesWithA = names.stream() .filter(name -> name.startsWith("A")) .collect(Collectors.toList());
2.7 Operator:接收和返回同样的类型
Operator接收和返回同样的类型,有很多种Operator:UnaryOperator BinaryOperator ,DoubleUnaryOperator, IntUnaryOperator, LongUnaryOperator,
DoubleBinaryOperator, IntBinaryOperator, LongBinaryOperator等。
@FunctionalInterface public interface IntUnaryOperator { /** * Applies this operator to the given operand. * * @param operand the operand * @return the operator result */ int applyAsInt(int operand);
我们看一个BinaryOperator的例子:
//Operator List<Integer> values = Arrays.asList(1, 2, 3, 4, 5); int sum = values.stream() .reduce(0, (i1, i2) -> i1 + i2);
3. Lambda表达式最佳实践
Lambda表达式java 8引入的函数式编程框架。之前的文章中我们也讲过Lambda表达式的基本用法。
本文将会在之前的文章基础上更加详细的讲解Lambda表达式在实际应用中的最佳实践经验。
3.1 优先使用标准Functional接口
之前的文章我们讲到了,java在java.util.function包中定义了很多Function接口。基本上涵盖了我们能够想到的各种类型。
假如我们自定义了下面的Functional interface:
@FunctionalInterface public interface Usage { String method(String string); }
然后我们需要在一个test方法中传入该interface:
public String test(String string, Usage usage) { return usage.method(string); }
上面我们定义的函数接口需要实现method方法,接收一个String,返回一个String。这样我们完全可以使用Function来代替:
public String test(String string, Function<String, String> fn) { return fn.apply(string); }
使用标准接口的好处就是,不要重复造轮子。
3.2 使用@FunctionalInterface注解
虽然@FunctionalInterface不是必须的,不使用@FunctionalInterface也可以定义一个Functional Interface。
但是使用@FunctionalInterface可以在违背Functional Interface定义的时候报警。
如果是在维护一个大型项目中,加上@FunctionalInterface注解可以清楚的让其他人了解这个类的作用。
从而使代码更加规范和更加可用。
所以我们需要这样定义:
@FunctionalInterface public interface Usage { String method(String string); }
而不是:
public interface Usage { String method(String string); }