Java 8 Lambda
一. Lambda表达式
1.几种常见的函数式接口
@FunctionalInterfacepublic 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);}
@FunctionalInterfacepublic interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);}
@FunctionalInterfacepublic interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);}
@FunctionalInterfacepublic interface Supplier<T> {/*** Gets a result.** @return a result*/T get();}
2.构造函数
使用无参数Supplier或者有参数Function构造,例如:
Supplier<Dish> dishSupplier = Dish::new;Dish dish = dishSupplier.get();Function<Integer, Dish> dishFunction = Dish::new;Dish dish = dishFunction.apply(10);BiFunction<Integer, String, Dish> dishFunction = Dish::new;Dish dish = dishFunction.apply(10, "gorson");
3.方法引用
方法的引用分为三种:
- 指向静态方法的方法引用,如Integer::parseInt;
- 指向任意类型实例方法的方法引用,如String::length;
- 指向现有对象的实例方法的方法引用,如xxx::getValue;
下面描述了四种类型的方法引用,如下所示。
种类 | 举例 |
静态方法引用 | ContainingClass::staticMethodName |
特定对象的实例方法引用 | ContainingObject::instanceMethodName |
特定类型的任意对象的实例方法引用 | ContainingType::methodName |
构造方法引用 | ClassName::new |
下面是方法引用的语法和具体的例子:
种类 | 语法 | 举例 |
静态方法引用 | Class::staticMethodName | String::valueOf |
特定对象的实例方法引用 | object::instanceMethodName | x::toString |
特定类型的任意对象的实例方法引用 | Class::instanceMethodName | String::toString |
构造方法引用 | ClassName::new | String::new |
最后,上面的方法引用等同于下面对应的 Lambda 表达式:
种类 | 语法 | Lambda |
静态方法引用 | Class::staticMethodName | (s) -> String.valueOf(s) |
特定对象的实例方法引用 | object::instanceMethodName | () -> "hello".toString() |
特定类型的任意对象的实例方法引用 | Class::instanceMethodName | (s) -> s.toString() |
构造方法引用 | ClassName::new | () -> new String() |
4.比较器复合:Comparing
逆序:reversed
比较器链:thenComparing
List<Dish> dishList = new ArrayList<>();dishList.sort(comparing(Dish::weight).reversed().thenComparing(Dish::getCountry));
5.谓词复合
- 返回的类型为:Predicate
- 非:negate
- 与:and
- 或:or
and和or方法是按照在表达式链中的位置,从左到右确定优先顺序
6.函数复合
Function接口提供了两个默认方法:andThen和compose,将Function接口所代表的Lambda表达式复合起来,最后返回一个Function的实例。
Function<Integer, Integer> f = x -> x + 1;Function<Integer, Integer> g = x -> x * 2;Function<Integer, Integer> h = f.compose(g); // h = f(g(x))Function<Integer, Integer> h = f.andThen(g); // h = g(f(x))
二. 引用流
Java 7 和Java 8 的比较:
public static void main(String... args) {// Java 7getLowCaloricDishesNamesInJava7(Dish.menu).forEach(System.out::println);// Java 8getLowCaloricDishesNamesInJava8(Dish.menu).forEach(System.out::println);}private static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes) {List<Dish> lowCaloricDishes = new ArrayList<>();for (Dish d : dishes) {if (d.getCalories() < 400) {lowCaloricDishes.add(d);}}List<String> lowCaloricDishesName = new ArrayList<>();Collections.sort(lowCaloricDishes, new Comparator<Dish>() {public int compare(Dish d1, Dish d2) {return Integer.compare(d1.getCalories(), d2.getCalories());}});for (Dish d : lowCaloricDishes) {lowCaloricDishesName.add(d.getName());}return lowCaloricDishesName;}private static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes) {return dishes.stream().filter(d -> d.getCalories() < 400).sorted(comparing(Dish::getCalories)).map(Dish::getName).collect(toList());}
三. 使用流
流的中间操作和终端操作:
操作 | 描述 | 类型 | 返回类型 | 使用类型,函数式接口 | 函数描述符 |
filter | 筛选 | 中间 | Stream | Predicate<T> | T->boolean |
distinct | 独一无二 | 中间(有状态-无界) | Stream | ||
skip | 跳过 | 中间(有状态-有界) | Stream | long | |
limit | 限制几个 | 中间(有状态-有界) | Stream | long | |
map | 获取…重新返回单独的流 | 中间 | Stream | Function<T, R> | T->R |
flatMap | 生成扁平化为单个流 | 中间 | Stream | Function<T, Stream<R>> | T->Stream |
sorted | 排序 | 中间(有状态-无界) | Stream | Comparator<T> | (T,T)->int |
anyMatch | 终端 | boolean | Predicate<T> | T->boolean | |
noneMatch | 终端 | boolean | Predicate<T> | T->boolean | |
allMatch | 终端 | boolean | Predicate<T> | T->boolean | |
findAny | 终端 | Optional | |||
findFirst | 终端 | Optional | |||
forEach | 终端 | void | Consumer<T> | T->void | |
collect | 终端 | R | Collector<T, A, R> | ||
reduce | 终端(有状态-有界) | Optional | BinaryOperator<T> | (T,T)->T | |
count | 终端 | long |
1.筛选和切片:filter,distinct,skip,limit
public static void main(String... args) {// Filtering with predicateList<Dish> vegetarianMenu = menu.stream().filter(Dish::isVegetarian) //过滤数据:Stream<T> filter(Predicate<? super T> predicate);.collect(toList());vegetarianMenu.forEach(System.out::println);// Filtering unique elementsList<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);numbers.stream().filter(i -> i % 2 == 0).distinct() //不包含相同的元素:Stream<T> distinct();.forEach(System.out::println);// Truncating a streamList<Dish> dishesLimit3 = menu.stream().filter(d -> d.getCalories() > 300).limit(3) //限制最大数目:Stream<T> limit(long maxSize);.collect(toList());dishesLimit3.forEach(System.out::println);// Skipping elementsList<Dish> dishesSkip2 = menu.stream().filter(d -> d.getCalories() > 300).skip(2) //忽略前面的数目:Stream<T> skip(long n);.collect(toList());dishesSkip2.forEach(System.out::println);}
2.映射:map,flatMap
public static void main(String... args) {//map:数据转换:<R> Stream<R> map(Function<? super T, ? extends R> mapper);//flatMap:数据扁平化转换:<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);// mapList<String> dishNames = menu.stream().map(Dish::getName).collect(toList());System.out.println(dishNames);// mapList<String> words = Arrays.asList("Hello", "World");List<Integer> wordLengths = words.stream().map(String::length).collect(toList());System.out.println(wordLengths);// flatMapwords.stream().flatMap((String line) -> Arrays.stream(line.split(""))).distinct().forEach(System.out::println);// flatMapList<Integer> numbers1 = Arrays.asList(1, 2, 3, 4, 5);List<Integer> numbers2 = Arrays.asList(6, 7, 8);List<int[]> pairs = numbers1.stream().flatMap((Integer i) -> numbers2.stream().map((Integer j) -> new int[]{i, j})).filter(pair -> (pair[0] + pair[1]) % 3 == 0).collect(toList());pairs.forEach(pair -> System.out.println("(" + pair[0] + ", " + pair[1] + ")"));}
3.查找和匹配:anyMatch,NoneMatch,AllMatch,findAny,findFirst
public class Dish {private final String name;private final boolean vegetarian;private final int calories;private final Type type;public Dish(String name, boolean vegetarian, int calories, Type type) {this.name = name;this.vegetarian = vegetarian;this.calories = calories;this.type = type;}public String getName() {return name;}public boolean isVegetarian() {return vegetarian;}public int getCalories() {return calories;}public Type getType() {return type;}public enum Type {MEAT, FISH, OTHER}@Overridepublic String toString() {return name;}public static final List<Dish> menu =Arrays.asList(new Dish("pork", false, 800, Dish.Type.MEAT),new Dish("beef", false, 700, Dish.Type.MEAT),new Dish("chicken", false, 400, Dish.Type.MEAT),new Dish("french fries", true, 530, Dish.Type.OTHER),new Dish("rice", true, 350, Dish.Type.OTHER),new Dish("season fruit", true, 120, Dish.Type.OTHER),new Dish("pizza", true, 550, Dish.Type.OTHER),new Dish("prawns", false, 400, Dish.Type.FISH),new Dish("salmon", false, 450, Dish.Type.FISH));}
public static void main(String... args) {if (isVegetarianFriendlyMenu()) {System.out.println("Vegetarian friendly");}System.out.println(isHealthyMenu());System.out.println(isHealthyMenu2());Optional<Dish> dish = findVegetarianDish();dish.ifPresent(d -> System.out.println(d.getName()));}private static boolean isVegetarianFriendlyMenu() {//包含任意一个,boolean anyMatch(Predicate<? super T> predicate);return menu.stream().anyMatch(Dish::isVegetarian);}private static boolean isHealthyMenu() {//全部满足条件:boolean allMatch(Predicate<? super T> predicate);return menu.stream().allMatch(d -> d.getCalories() < 1000);}private static boolean isHealthyMenu2() {//没有满足条件的:boolean noneMatch(Predicate<? super T> predicate);return menu.stream().noneMatch(d -> d.getCalories() >= 1000);}private static Optional<Dish> findVegetarianDish() {//返回任意一个:Optional<T> findAny();return menu.stream().filter(Dish::isVegetarian).findAny();}
4.归约:将两个元素结合起来产生新值:reduce,可用于计算最大值,最小值和总和
public static void main(String...args) {//T reduce(T identity, BinaryOperator<T> accumulator);List<Integer> numbers = Arrays.asList(3, 4, 5, 1, 2);int sum = numbers.stream().reduce(0, (a, b) -> a + b);System.out.println(sum);int sum2 = numbers.stream().reduce(0, Integer::sum);System.out.println(sum2);int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b));System.out.println(max);Optional<Integer> min = numbers.stream().reduce(Integer::min);min.ifPresent(System.out::println);int calories = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);System.out.println("Number of calories:" + calories);}
5.原始类型:IntStream,DoubleStream,LongStream
Java 8 引入三个原始类型特化流接口来解决原始类型的问题:IntStream,DoubleStream,LongStream,从而避免暗含的装箱成本。
List<Integer> numbers = Arrays.asList(3, 4, 5, 1, 2);//public static <T> Stream<T> stream(T[] array)Arrays.stream(numbers.toArray()).forEach(System.out::println);int calories = menu.stream().mapToInt(Dish::getCalories).sum();System.out.println("Number of calories:" + calories);// max and OptionalIntOptionalInt maxCalories = menu.stream().mapToInt(Dish::getCalories).max();int max;if (maxCalories.isPresent()) {max = maxCalories.getAsInt();} else {// we can choose a default valuemax = 1;}System.out.println(max);// numeric rangesIntStream evenNumbers = IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0);System.out.println(evenNumbers.count());
6.创建流
- Stream.of传参实例化流;
- Stream.empty返回空流;
- Array.stream(List<T>)将List转为Stream<T>;
- Stream.iterate和Stream.generate两个操作可以创建无限流。
// Stream.ofStream<String> stream = Stream.of("Java 8", "Lambdas", "In", "Action");stream.map(String::toUpperCase).forEach(System.out::println);// Stream.emptyStream<String> emptyStream = Stream.empty();// Arrays.streamint[] numbers = {2, 3, 5, 7, 11, 13};System.out.println(Arrays.stream(numbers).sum());// Stream.iterate,创建无限流,一般使用limit作为限制,seed作为初始值,新的数值使用旧的结果作为输入//public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);// fibonnaci with iterateStream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]}).limit(10).forEach(t -> System.out.println("(" + t[0] + ", " + t[1] + ")"));Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]}).limit(10).map(t -> t[0]).forEach(System.out::println);// random stream of doubles with Stream.generateStream.generate(Math::random).limit(10).forEach(System.out::println);// stream of 1s with Stream.generateIntStream.generate(() -> 1).limit(5).forEach(System.out::println);IntStream.generate(new IntSupplier() {public int getAsInt() {return 2;}}).limit(5).forEach(System.out::println);
7.练习示例:
public static void main(String... args) {//Name, CityTrader raoul = new Trader("Raoul", "Cambridge");Trader mario = new Trader("Mario", "Milan");Trader alan = new Trader("Alan", "Cambridge");Trader brian = new Trader("Brian", "Cambridge");//Trader, Year, ValueList<Transaction> transactions = Arrays.asList(new Transaction(brian, 2011, 300),new Transaction(raoul, 2012, 1000),new Transaction(raoul, 2011, 400),new Transaction(mario, 2012, 710),new Transaction(mario, 2012, 700),new Transaction(alan, 2012, 950));//1.找出2011年发生的所有交易,并按交易额排序List<Transaction> tr2011 = transactions.stream().filter(transaction -> transaction.getYear() == 2011).sorted(comparing(Transaction::getValue)).collect(toList());System.out.println(tr2011);//2.交易员都在哪些不同的城市工作过List<String> cities = transactions.stream().map(transaction -> transaction.getTrader().getCity()).distinct().collect(toList());System.out.println(cities);//3.查找所有来自剑桥的交易员,并按姓名排序List<Trader> traders = transactions.stream().map(Transaction::getTrader).filter(trader -> trader.getCity().equals("Cambridge")).distinct().sorted(comparing(Trader::getName)).collect(toList());System.out.println(traders);//4.查找所有交易员的姓名字符串,并按姓名排序String traderStr = transactions.stream().map(transaction -> transaction.getTrader().getName()).distinct().sorted().reduce("", (n1, n2) -> n1 + n2);System.out.println(traderStr);//5.有没有交易员是米兰工作的boolean milanBased = transactions.stream().anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan"));System.out.println(milanBased);//6.将米兰的交易员修改为剑桥transactions.stream().map(Transaction::getTrader).filter(trader -> trader.getCity().equals("Milan")).forEach(trader -> trader.setCity("Cambridge"));System.out.println(transactions);//7.所有交易中,最高的交易额是多少int highestValue = transactions.stream().map(Transaction::getValue).reduce(0, Integer::max);System.out.println(highestValue);}}
四. 用流收集数据:collect
Collectors收集器静态方法:
名称 | 返回类型 | 用于 | 使用示例 |
toList() | List<T> | 将流中所有项目收集到一个List | List<T> l = stream.collect(toList()); |
toSet() | Set<T> | 将流中所有项目收集到一个Set | Set<T> s = stream.collect(toSet()); |
toCollection() | Collection<T> | 将数据转换为指定的集合 |
Collection<T> c = stream.collect(toCollection(), ArrayList::new);
Collection<T> c = stream.collect(toCollection(), HashSet::new);
|
counting() | Long | 计算流中数量 | long l = stream.collect(counting()); |
summingInt() | Integer | 返回流中整数属性求和 | int i = stream.collect(summingInt(T::getXX)); |
averagingInt() | Double | 计算流中Integer属性的平均值 | double d = stream.collect(averagingInt(T::getXX)); |
summarizingInt() | IntSummaryStatistics | 收集流中Integer属性的统计值 | IntSummaryStatistics i = stream.collect(summarizingInt(T::getXX)); |
joining() | String | 将流中每个元素调用toString方法拼接 | String s = stream.collect(joining(" , "); |
maxBy() | Optional<T> | 筛选流中最大元素的Optional,流为空则Optional.empty() | Optional<T> o = stream.collect(maxBy(Comparator.comparingInt(T::getXX))); |
minBy() | Optional<T> | 筛选流中最小元素的Optional,流为空则Optional.empty() | Optional<T> o = stream.collect(minBy(Comparator.comparingInt(T::getXX))); |
reducing() | 归约操作产生的类型 | 从初始值开始,利用BinaryOperator与流中每个元素相结合,从而将流归约成单个值 | int i = stream.collect(reducing(0, T::getXX, Integer :: sum)); |
collectingAndThen() | 转换函数返回的类型 | 包裹一个收集器,对其结果应用转换函数 | int i = stream.collect(collectingAndThen(toList(),List :: size); |
groupingBy() | Map<K,List<T>> | 根据流中一个属性的值做分组,并以该属性为Map的一个Key | Map<T.XX,List<T>> map = stream.collect(groupingBy(T::getXX)); |
partitioningBy() | Map<Boolean,List<T>> | 根据对流中的每个元素的应用谓词的结果进行分区(true,false) | Map<Boolean,List<T>> map = stream.collect(partitioningBy(T::isXX)); |
1.数值类型收集器:maxBy,minBy,summingInt,averagingInt,summarizingInt
Comparator<Dish> dishComparator = Comparator.comparingInt(Dish::getCalories);Optional<Dish> minCaloriesDish = menu.stream().collect(minBy(dishComparator));Optional<Dish> maxCaloriesDish = menu.stream().collect(maxBy(dishComparator));double averageDouble = menu.stream().collect(averagingDouble(Dish::getCalories));int averageInt = menu.stream().collect(summingInt(Dish::getCalories));IntSummaryStatistics intSummaryStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
2.String收集器:joining
private static String getShortMenu() {return menu.stream().map(Dish::getName).collect(joining());}private static String getShortMenuCommaSeparated() {return menu.stream().map(Dish::getName).collect(joining(", "));}
3.广义的归约:reducing
事实上,前面使用的收集器,都是一个可以用reducing工厂方法定义的归约过程的特殊情况。Collectors.reducing工厂方法是所有这些特殊情况的一般化。
public static <T, U> Collector<T, ?, U> reducing(U identity, //起始值Function<? super T, ? extends U> mapper, //规约函数BinaryOperator<U> op) //将两个项目积累成一个同类型的值
示例:
private static int calculateTotalCalories() {return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i + j));}private static int calculateTotalCaloriesWithMethodReference() {return menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum));}private static int calculateTotalCaloriesWithoutCollectors() {return menu.stream().map(Dish::getCalories).reduce(Integer::sum).get();}private static int calculateTotalCaloriesUsingSum() {return menu.stream().mapToInt(Dish::getCalories).sum();}
4.分组收集:groupingBy
groupingBy使用Function作为分类函数,用来将流中的元素分类成不同的组。
(1)函数类型
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
public static <T, K, D, A, M extends Map<K, D> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,Supplier<M> mapFactory,Collector<? super T, A, D> downstream)
(2)一级分组
private static Map<Dish.Type, List<Dish>> groupDishesByType() {return menu.stream().collect(groupingBy(Dish::getType));}private static Map<Dish.Type, List<String>> groupDishNamesByType() {return menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList())));}
(3)多级分组
private static Map<CaloricLevel, List<Dish>> groupDishesByCaloricLevel() {return menu.stream().collect(groupingBy(dish -> {if (dish.getCalories() <= 400) return CaloricLevel.DIET;else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;else return CaloricLevel.FAT;} ));}private static Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishedByTypeAndCaloricLevel() {return menu.stream().collect(groupingBy(Dish::getType,groupingBy((Dish dish) -> {if (dish.getCalories() <= 400) return CaloricLevel.DIET;else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;else return CaloricLevel.FAT;} )));}
(4)按子组收集数据
private static Map<Dish.Type, Long> countDishesInGroups() {return menu.stream().collect(groupingBy(Dish::getType, counting()));}private static Map<Dish.Type, Optional<Dish>> mostCaloricDishesByType() {return menu.stream().collect(groupingBy(Dish::getType,reducing((Dish d1, Dish d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)));}
将收集器的结果转换成另一种类型:collectingAndThen:
private static Map<Dish.Type, Dish> mostCaloricDishesByTypeWithoutOprionals() {return menu.stream().collect(groupingBy(Dish::getType,collectingAndThen(reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2),Optional::get))); //转换函数Optional::get把返回的Optional中的值提取出来,因为reducing不会返回Optional.empty}
与groupingBy联合使用的其他收集器例子:
通过groupingBy工厂方法的第二个参数传递的收集器将会对分到同一组中的所有流元素执行进一步的规约操作。常见的收集器为mapping方法,该方法接收两个参数:一个函数对流中的元素做转换,另一个则将转换的结果对象收集起来。
private static Map<Dish.Type, Integer> sumCaloriesByType() {return menu.stream().collect(groupingBy(Dish::getType,summingInt(Dish::getCalories)));}private static Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType() {return menu.stream().collect(groupingBy(Dish::getType, mapping(dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;else return CaloricLevel.FAT; },toSet() )));}
5.分区收集:partitioningBy
将数据分为true和false的数据区域,支持功能和groupingBy一样:
private static Map<Boolean, List<Dish>> partitionByVegeterian() {return menu.stream().collect(partitioningBy(Dish::isVegetarian));}private static Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType() {return menu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));}private static Object mostCaloricPartitionedByVegetarian() {return menu.stream().collect(partitioningBy(Dish::isVegetarian,collectingAndThen(maxBy(comparingInt(Dish::getCalories)),Optional::get)));}
6.收集器接口
public interface Collector<T, A, R> {/*** A function that creates and returns a new mutable result container.** @return a function which returns a new, mutable result container*/Supplier<A> supplier();/*** A function that folds a value into a mutable result container.** @return a function which folds a value into a mutable result container*/BiConsumer<A, T> accumulator();/*** A function that accepts two partial results and merges them. The* combiner function may fold state from one argument into the other and* return that, or may return a new result container.** @return a function which combines two partial results into a combined* result*/BinaryOperator<A> combiner();/*** Perform the final transformation from the intermediate accumulation type* {@code A} to the final result type {@code R}.** <p>If the characteristic {@code IDENTITY_TRANSFORM} is* set, this function may be presumed to be an identity transform with an* unchecked cast from {@code A} to {@code R}.** @return a function which transforms the intermediate result to the final* result*/Function<A, R> finisher();/*** Returns a {@code Set} of {@code Collector.Characteristics} indicating* the characteristics of this Collector. This set should be immutable.** @return an immutable set of collector characteristics*/Set<Characteristics> characteristics();/*** Returns a new {@code Collector} described by the given {@code supplier},* {@code accumulator}, and {@code combiner} functions. The resulting* {@code Collector} has the {@code Collector.Characteristics.IDENTITY_FINISH}* characteristic.** @param supplier The supplier function for the new collector* @param accumulator The accumulator function for the new collector* @param combiner The combiner function for the new collector* @param characteristics The collector characteristics for the new* collector* @param <T> The type of input elements for the new collector* @param <R> The type of intermediate accumulation result, and final result,* for the new collector* @throws NullPointerException if any argument is null* @return the new {@code Collector}*/public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,BiConsumer<R, T> accumulator,BinaryOperator<R> combiner,Characteristics... characteristics) {Objects.requireNonNull(supplier);Objects.requireNonNull(accumulator);Objects.requireNonNull(combiner);Objects.requireNonNull(characteristics);Set<Characteristics> cs = (characteristics.length == 0)? Collectors.CH_ID: Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH,characteristics));return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, cs);}/*** Returns a new {@code Collector} described by the given {@code supplier},* {@code accumulator}, {@code combiner}, and {@code finisher} functions.** @param supplier The supplier function for the new collector* @param accumulator The accumulator function for the new collector* @param combiner The combiner function for the new collector* @param finisher The finisher function for the new collector* @param characteristics The collector characteristics for the new* collector* @param <T> The type of input elements for the new collector* @param <A> The intermediate accumulation type of the new collector* @param <R> The final result type of the new collector* @throws NullPointerException if any argument is null* @return the new {@code Collector}*/public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,BiConsumer<A, T> accumulator,BinaryOperator<A> combiner,Function<A, R> finisher,Characteristics... characteristics) {Objects.requireNonNull(supplier);Objects.requireNonNull(accumulator);Objects.requireNonNull(combiner);Objects.requireNonNull(finisher);Objects.requireNonNull(characteristics);Set<Characteristics> cs = Collectors.CH_NOID;if (characteristics.length > 0) {cs = EnumSet.noneOf(Characteristics.class);Collections.addAll(cs, characteristics);cs = Collections.unmodifiableSet(cs);}return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, finisher, cs);}/*** Characteristics indicating properties of a {@code Collector}, which can* be used to optimize reduction implementations.*/enum Characteristics {/*** Indicates that this collector is <em>concurrent</em>, meaning that* the result container can support the accumulator function being* called concurrently with the same result container from multiple* threads.** <p>If a {@code CONCURRENT} collector is not also {@code UNORDERED},* then it should only be evaluated concurrently if applied to an* unordered data source.*/CONCURRENT,/*** Indicates that the collection operation does not commit to preserving* the encounter order of input elements. (This might be true if the* result container has no intrinsic order, such as a {@link Set}.)*/UNORDERED,/*** Indicates that the finisher function is the identity function and* can be elided. If set, it must be the case that an unchecked cast* from A to R will succeed.*/IDENTITY_FINISH}}