Java8笔记(2)

Jenkins保姆笔记(2)——基于Java8的Jenkins插件安装 前面我们介绍过:本篇主要介绍下基于Java8的Jenkins插件安装。为什么要单独讲一个插件安装?因为一些原因,Jenkins自带的插件源下载几乎都会失败,我们讲就是讲如何修改插件的源。 阅读详情

Java8笔记(2)

用流收集数据

收集器

函数式编程相对于指令式编程的一个主要优势:你只需指出希望的结果——“做什么”,而不用操心执行的步骤——“如何做”

收集器用作高级归约

收集器非常有用,因为用它可以简洁而灵活地定义 collect 用来生成结果集合的标准。更具体地说,对流调用collect 方法将对流中的元素触发一个归约操作(由 Collector 来参数化)

一般来说, Collector 会对元素应用一个转换函数(很多时候是不体现任何效果的恒等转换,例如 toList ),并将结果累积在一个数据结构中,从而产生这一过程的最终输出

预定义收集器

预定义收集器的功能,也就是那些可以从 Collectors类提供的工厂方法(例如 groupingBy )创建的收集器。它们主要提供了三大功能

  • 将流元素归约和汇总为一个值

  • 元素分组

  • 元素分区

归约和汇总

在需要将流项目重组成集合时,一般会使用收集器( Stream 方法 collect的参数)。再宽泛一点来说,但凡要把流中所有的项目合并成一个结果时就可以用。这个结果可以是任何类型,可以复杂如代表一棵树的多级映射,或是简单如一个整数


public class M1 {


    public static void main(String[] args) {

        List<Dish> menu  = Data.create();

//        利用 counting 工厂方法返回的收集器,数一数菜单里有多少
//种菜:

        long howManyDishes = menu.stream()
                .collect(Collectors.counting());

        System.out.println(howManyDishes);

//        还可以写得更为直接:
        long howManyDishes_1 = menu.stream()
                .count();

        System.out.println(howManyDishes_1);


    }
}

查找流中的最大值和最小值

假设你想要找出菜单中热量最高的菜。你可以使用两个收集器, Collectors.maxBy 和
Collectors.minBy ,来计算流中的最大或最小值

这两个收集器接收一个 Comparator 参数来比较流中的元素。你可以创建一个 Comparator 来根据所含热量对菜肴进行比较,并把它传递给Collectors.maxBy

public class M1 {


    public static void main(String[] args) {

        List<Dish> menu  = Data.create();


        Comparator<Dish> dishCaloriesComparator =
                Comparator.comparingInt(Dish::getCalories);

//        System.out.println(dishCaloriesComparator);

        Optional<Dish> mostCalorieDish =
                menu.stream()
                        .collect(maxBy(dishCaloriesComparator));


        System.out.println(mostCalorieDish.get());
    }
}


汇总

Collectors 类专门为汇总提供了一个工厂方法: Collectors.summingInt 。它可接受一
个把对象映射为求和所需 int 的函数,并返回一个收集器;该收集器在传递给普通的collect方法后即执行我们需要的汇总操作

public class M1 {


    public static void main(String[] args) {

        List<Dish> menu = Data.create();

        
//你可以这样求出菜单列表的总热量
        int totalCalories = menu.stream()
                .collect(summingInt(Dish::getCalories));

        System.out.println(totalCalories);

    }
}


但汇总不仅仅是求和;还有 Collectors.averagingInt ,连同对应的 averagingLong 和
averagingDouble 可以计算数值的平均数:


public class M2 {

    public static void main(String[] args) {

        List<Dish> menu = Data.create();
        double avgCalories = menu.stream()
                .collect(averagingInt(Dish::getCalories));

        System.out.println(avgCalories);

    }
}

你已经看到了如何使用收集器来给流中的元素计数,找到这些元素数值属性的最大值和最小值,以及计算其总和和平均值

不过很多时候,你可能想要得到两个或更多这样的结果,而且你希望只需一次操作就可以完成。在这种情况下,你可以使用 summarizingInt 工厂方法返回的收集器。例如,通过一次 summarizing 操作你可以就数出菜单中元素的个数,并得到菜肴热量总和、平均值、最大值和最小值:



public class M3 {


    public static void main(String[] args) {

        List<Dish> menu = Data.create();

        IntSummaryStatistics  menuStatistics =

                menu.stream()
                .collect(summarizingInt(Dish::getCalories));

        System.out.println(menuStatistics);
//IntSummaryStatistics{count=9, sum=4200, min=120, average=466.666667, max=800}

    }
}


同样,相应的 summarizingLong 和 summarizingDouble 工厂方法有相关的 LongSummary-
Statistics 和 DoubleSummaryStatistics 类型,适用于收集的属性是原始类型 long 或double 的情况

连接字符串

joining 工厂方法返回的收集器会把对流中每一个对象应用 toString 方法得到的所有字符串连接成一个字符串


public class M1 {


    public static void main(String[] args) {
//把菜单中所有菜肴的名称连接起来

        List<Dish> menu = Data.create();

        String shortMenu = menu.stream()
                .map(Dish::getName).collect(joining());


        System.out.println(shortMenu);


    }
}

该字符串的可读性并不好。幸好, joining 工厂方法有一个重载版本可以接受元素之间的
分界符,这样你就可以得到一个逗号分隔的菜肴名称列表:


 String shortMenu_1 = menu.stream()
                .map(Dish::getName)
                .collect(joining(", "));

        System.out.println(shortMenu_1);

广义的归约汇总

事实上,我们已经讨论的所有收集器,都是一个可以用 reducing 工厂方法定义的归约过程
的特殊情况而已。 Collectors.reducing 工厂方法是所有这些特殊情况的一般化。可以说,先前讨论的案例仅仅是为了方便程序员而已

可以用 reducing 方法创建的收集器来计算你菜单的总热量


    public static void main(String[] args) {

        List<Dish> menu = Data.create();

        int totalCalories = menu.stream()
                .collect(reducing(
                        0,
                        Dish::getCalories,
                        (i,j) -> i + j

                ));

        System.out.println(totalCalories);


    }

需要三个参数

  • 第一个参数是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值
    和而言 0 是一个合适的值

  • 第二个参数就一个功能函数,将菜肴转换成一个表示其所含热量的 int

  • 第三个参数是一个 BinaryOperator ,将两个项目累积成一个同类型的值。这里它就是
    对两个 int 求和

同样,你可以使用下面这样单参数形式的 reducing 来找到热量最高的菜

    public static void main(String[] args) {

        List<Dish> menu = Data.create();

        Optional<Dish> mostCalorieDish =

                menu.stream()
                .collect(reducing(
                        (d1,d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2
                ));

        System.out.println(mostCalorieDish);

    }


收集框架的灵活性:以不同的方法执行同样的操作

简化前面使用 reducing 收集器的求和例子——引用 Integer 类的 sum 方法,而不用去写一个表达同一操作的Lambda表达式



 public static void main(String[] args) {

        List<Dish> menu = Data.create();

        int totalCalories = menu.stream()
                .collect(reducing(
                        0,
                        Dish::getCalories,
                        Integer::sum
                ));


        System.out.println(totalCalories);


    }

分组

一个常见的数据库操作是根据一个或多个属性对集合中的项目进行分组

假设你要把菜单中的菜按照类型进行分类,有肉的放一组,有鱼的放一组,其他的都放另一组。用 Collectors.groupingBy 工厂方法返回的收集器就可以轻松地完成这项任务


  public static void main(String[] args) {

        List<Dish> menu = Data.create();


        Map<Dish.Type,List<Dish>> dishesByType =

                menu.stream()
                .collect(groupingBy(Dish::getType));

        System.out.println(dishesByType);

        for (Dish.Type type : dishesByType.keySet()){

            System.out.println(type + " :");
            System.out.println(dishesByType.get(type));
            System.out.println("");
        }


    }

这里,你给 groupingBy 方法传递了一个 Function (以方法引用的形式),它提取了流中每一道 Dish 的 Dish.Type 。我们把这个 Function 叫作分类函数,因为它用来把流中的元素分成不同的组

但是,分类函数不一定像方法引用那样可用,因为你想用以分类的条件可能比简单的属性访
问器要复杂

例如,你可能想把热量不到400卡路里的菜划分为“低热量”(diet),热量400到700卡路里的菜划为“普通”(normal),高于700卡路里的划为“高热量”(fat)。由于 Dish 类的作者没有把这个操作写成一个方法,你无法使用方法引用,但你可以把这个逻辑写成Lambda表达式:

public enum CaloricLevel { DIET, NORMAL, FAT }





public static void main(String[] args) {

        List<Dish> menu = Data.create();


        Map<CaloricLevel, List<Dish>> dishesByCaloricLevel =

                menu.stream()
                .collect(

                        groupingBy(dish ->
                        {
                            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                            else if (dish.getCalories() <= 700) return  CaloricLevel.NORMAL;
                            else return CaloricLevel.FAT;
                        })

                );


        System.out.println(dishesByCaloricLevel);


    }

多级分组

要实现多级分组,我们可以使用一个由双参数版本的 Collectors.groupingBy 工厂方法创
建的收集器,它除了普通的分类函数之外,还可以接受 collector 类型的第二个参数。那么要进行二级分组的话,我们可以把一个内层 groupingBy 传递给外层 groupingBy ,并定义一个为流中项目分类的二级标准


public class M1 {

    public static void main(String[] args) {

        List<Dish> menu = Data.create();


        Map<Dish.Type,Map<CaloricLevel,List<Dish>>> dishesByTypeCaloricLevel =

                menu.stream()
                .collect(
                        groupingBy(Dish::getType,
                                groupingBy(dish ->
                                {
                                    if (dish.getCalories() <=400) return CaloricLevel.DIET;
                                    else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                                    else return CaloricLevel.FAT;
                                }))
                );


        System.out.println(dishesByTypeCaloricLevel);
//        {
//        OTHER={
//        NORMAL=
//        [Dish
//        {name='french fries', vegetarian=true, calories=530, type=OTHER}, Dish{name='pizza', vegetarian=true, calories=550, type=OTHER}], DIET=[Dish{name='rice', vegetarian=true, calories=350, type=OTHER}, Dish{name='season fruit', vegetarian=true, calories=120, type=OTHER}]}, FISH={NORMAL=[Dish{name='salmon', vegetarian=false, calories=450, type=FISH}], DIET=[Dish{name='prawns', vegetarian=false, calories=300, type=FISH}]}, MEAT={FAT=[Dish{name='pork', vegetarian=false, calories=800, type=MEAT}], NORMAL=[Dish{name='beef', vegetarian=false, calories=700, type=MEAT}], DIET=[Dish{name='chicken', vegetarian=false, calories=400, type=MEAT}]}}


//        两级 Map


//        这里的外层 Map 的键就是第一级分类函数生成的值:“fish, meat, other”,而这个 Map 的值又是
//一个 Map ,键是二级分类函数生成的值:“normal, diet, fat”。最后,第二级 map 的值是流中元素构
//成的 List ,是分别应用第一级和第二级分类函数所得到的对应第一级和第二级键的值:“salmon、
//pizza…” 这种多级分组操作可以扩展至任意层级,n级分组就会得到一个代表n级树形结构的n级
//Map 。


    }
}


按子组收集数据

传递给第一个 groupingBy 的第二个收集器可以是任何类型,而不一定是另一个groupingBy

数一数菜单中每类菜有多少个,可以传递 counting 收集器作为groupingBy 收集器的第二个参数


    public static void main(String[] args) {

        List<Dish> menu = Data.create();

        Map<Dish.Type, Long> typesCount = menu.stream().collect(
                groupingBy(Dish::getType, counting()));


        System.out.println(typesCount);


    }

把前面用于查找菜单中热量最高的菜肴的收集器改一改,按照菜的类型分类


  public static void main(String[] args) {

        List<Dish> menu = Data.create();

        Map<Dish.Type, Optional<Dish>> mostCaloricByType =

                menu.stream()
                .collect(
                        groupingBy(Dish::getType
                        ,
                                maxBy(Comparator.comparingInt(Dish::getCalories)))
                );


        System.out.println(mostCaloricByType);
    }


这个分组的结果显然是一个 map ,以 Dish 的类型作为键,以包装了该类型中热量最高的 Dish的 Optional 作为值

把收集器的结果转换为另一种类型

一般地来说,把收集器返回的结果转换为另一种类型,你可以使用Collectors.collectingAndThen 工厂方法返回的收集器

查找每个子组中热量最高的 Dish



    public static void main(String[] args) {

        List<Dish> menu = Data.create();


        Map<Dish.Type, Dish> mostCaloricByType =

                menu.stream()
                .collect(groupingBy(Dish::getType,
                        collectingAndThen(
                                maxBy(Comparator.comparingInt(Dish::getCalories)),
                                Optional::get
                        )));


        System.out.println(mostCaloricByType);

//        {

//        OTHER=Dish{name='pizza', vegetarian=true, calories=550, type=OTHER},

//        FISH=Dish{name='salmon', vegetarian=false, calories=450, type=FISH},

//        MEAT=Dish{name='pork', vegetarian=false, calories=800, type=MEAT}}



    }

这个工厂方法接受两个参数——要转换的收集器以及转换函数,并返回另一个收集器。这个
收集器相当于旧收集器的一个包装, collect 操作的最后一步就是将返回值用转换函数做一个映射。在这里,被包起来的收集器就是用 maxBy 建立的那个,而转换函数Optional::get 则把返回的 Optional 中的值提取出来。前面已经说过,这个操作放在这里是安全的,因为 reducing收集器永远都不会返回 Optional.empty()

与 groupingBy 联合使用的其他收集器的例子

一般来说,通过 groupingBy 工厂方法的第二个参数传递的收集器将会对分到同一组中的所
有流元素执行进一步归约操作


  public static void main(String[] args) {


//求出所有菜肴热量总和的收集器,不过这次是对
//每一组 Dish 求和

        List<Dish> menu = Data.create();

        Map<Dish.Type, Integer> totalCaloriesByType =

                menu.stream()
                .collect(groupingBy(Dish::getType,
                        summingInt(Dish::getCalories)));


        System.out.println(totalCaloriesByType);



    }

然而常常和 groupingBy 联合使用的另一个收集器是 mapping 方法生成的。这个方法接受两个参数:一个函数对流中的元素做变换,另一个则将变换的结果对象收集起来。其目的是在累加之前对每个输入元素应用一个映射函数,这样就可以让接受特定类型元素的收集器适应不同类型的对象

比方说你想要知道,对于每种类型的 Dish ,菜单中都有哪些 CaloricLevel 。我们可以把 groupingBy 和 mapping 收集器结合起来


  public static void main(String[] args) {

        List<Dish> menu = Data.create();
        Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =

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


        System.out.println(caloricLevelsByType);

    }

这里,就像我们前面见到过的,传递给映射方法的转换函数将 Dish 映射成了它的CaloricLevel :生成的 CaloricLevel 流传递给一个 toSet 收集器,它和 toList 类似,不过是把流中的元素累积到一个 Set 而不是 List 中,以便仅保留各不相同的值

分区

分区是分组的特殊情况:由一个谓词(返回一个布尔值的函数)作为分类函数,它称分区函数。分区函数返回一个布尔值,这意味着得到的分组 Map 的键类型是 Boolean ,于是它最多可以分为两组—— true 是一组, false 是一组

把菜单按照素食和非素食分开:



public static void main(String[] args) {

        List<Dish> menu = Data.create();
        Map<Boolean, List<Dish>> partitionedMenu =

                menu.stream()
                .collect(partitioningBy(Dish::isVegetarian));

        System.out.println(partitionedMenu);

    }

那么通过 Map 中键为 true 的值,就可以找出所有的素食菜肴了:


List<Dish> vegetarianDishes = partitionedMenu.get(true);


用同样的分区谓词,对菜单 List 创建的流作筛选,然后把结果收集到另外一个 List中也可以获得相同的结果



 public static void main(String[] args) {

        List<Dish> menu = Data.create();

        List<Dish> vegetarianDishes =
                menu.stream().filter(Dish::isVegetarian).collect(toList());

        System.out.println(vegetarianDishes);

    }

分区的优势

分区的好处在于保留了分区函数返回 true 或 false 的两套流元素列表

partitioningBy工厂方法有一个重载版本,可以像下面这样传递第二个收集器


 public static void main(String[] args) {

        List<Dish> menu = Data.create();
        Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =

                menu.stream()
                .collect(
                        partitioningBy(
                                Dish::isVegetarian,
                                groupingBy(Dish::getType)
                        )
                );


        System.out.println(vegetarianDishesByType);
    }
    
//    这里,对于分区产生的素食和非素食子流,分别按类型对菜肴分组,得到了一个二级 Map 
    

重用前面的代码来找到素食和非素食中热量最高的菜



 public static void main(String[] args) {

        List<Dish> menu = Data.create();

        Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
                menu.stream().collect(
                        partitioningBy(Dish::isVegetarian,
                                collectingAndThen(
                                        maxBy(comparingInt(Dish::getCalories)),
                                        Optional::get)));

        System.out.println(mostCaloricPartitionedByVegetarian);
    }

将数字按质数和非质数分区

写一个方法,它接受参数 int n,并将前n个自然数分为质数和非质数
  • 首先,找出能够测试某一个待测数字是否是质数的谓词

//  第一个版本
    public boolean isPrime(int candidate){

//        产生一个自然数//范围,从2开始,//直至但不包括待//测数
        return IntStream.range(2,candidate)
                .noneMatch(i -> candidate % i == 0);

    }

//    优化版本
    public boolean isPrime_2(int candidate) {
        int candidateRoot = (int) Math.sqrt((double) candidate);
        return IntStream.rangeClosed(2, candidateRoot)
                .noneMatch(i -> candidate % i == 0);
    }

最终实现:

  public static Map<Boolean, List<Integer>> partitionPrimes(int n) {
        return IntStream.rangeClosed(2, n).boxed()
                .collect(
                        partitioningBy(candidate -> isPrime_2(candidate)));
    }

收集器接口

Collector 接口包含了一系列方法,为实现具体的归约操作(即收集器)提供了范本。我们
已经看过了 Collector 接口中实现的许多收集器,例如 toList 或 groupingBy 。这也意味着,你可以为 Collector 接口提供自己的实现,从而自由地创建自定义归约操作

toList 工厂方法,它会把流中的所有元素收集成一个 List

Collector 接口

public interface Collector<T, A, R> {
       Supplier<A> supplier();
       BiConsumer<A, T> accumulator();
       Function<A, R> finisher();
       BinaryOperator<A> combiner();
       Set<Characteristics> characteristics();
}

本列表适用以下定义:

  • T 是流中要收集的项目的泛型。
  • A 是累加器的类型,累加器是在收集过程中用于累积部分结果的对象。
  • R 是收集操作得到的对象(通常但并不一定是集合)的类型

Collector 接口声明的方法

建立新的结果容器: supplier 方法

supplier 方法必须返回一个结果为空的 Supplier ,也就是一个无参数函数,在调用时它会创建一个空的累加器实例,供数据收集过程使用

将元素添加到结果容器: accumulator 方法

accumulator 方法会返回执行归约操作的函数。当遍历到流中第n个元素时,这个函数执行
时会有两个参数:保存归约结果的累加器(已收集了流中的前 n1 个项目),还有第n个元素本身。该函数将返回 void ,因为累加器是原位更新,即函数的执行改变了它的内部状态以体现遍历的元素的效果

对结果容器应用最终转换: finisher 方法

在遍历完流后, finisher 方法必须返回在累积过程的最后要调用的一个函数,以便将累加器对象转换为整个集合操作的最终结果

合并两个结果容器: combiner 方法

四个方法中的最后一个—— combiner 方法会返回一个供归约操作使用的函数,它定义了对流
的各个子部分进行并行处理时,各个子部分归约所得的累加器要如何合并

characteristics 方法

最后一个方法—— characteristics 会返回一个不可变的 Characteristics 集合,它定义了收集器的行为——尤其是关于流是否可以并行归约,以及可以使用哪些优化的提示

全部融合到一起

//开发自己的 ToListCollector
public class ToListCollector<T> implements Collector<T, List<T>,List<T>> {


//    创建集合操//作的起始点
    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }


//    累 积 遍 历 过的//项目,原位修改//累加器
    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return List::add;
    }



    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1,list2) ->{

//            修 改 第 一 个 累 加//器,将其与第二个//累加器的内容合并
            list1.addAll(list2);

//            返回修改后的//第一个累加器
            return list1;
        };
    }


//    恒等//函数
    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity();
    }

//    为收集器添加 IDENTITY//_FINISH 和 CONCURRENT 标志
    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(
                IDENTITY_FINISH, CONCURRENT));
    }


//    这个实现与 Collectors.toList 方法并不完全相同,但区别仅仅是一些小的优化。
//这些优化的一个主要方面是Java API所提供的收集器在需要返回空列表时使用了 Collections.
//emptyList() 这个单例(singleton)


}


开发自己的收集器以获得更好的性能

对 将前n个自然数划分为质数和非质数 这个例子进行优化

仅用质数做除数

一个可能的优化是仅仅看看被测试数是不是能够被质数整除。要是除数本身都不是质数就用不着测了

必须自己开发一个收集器的原因在于,在收集过程中是没有办法访问部分结果的。这意味着,当测试某一个数字是否是质数的时候,你没法访问目前已经找到的其他质数的列表

  • 需要想办法在下一个质数大于被测数平方根时立即停止测试

创建一个名为 takeWhile 的方法,给定一个排序列表和一个谓词,它会返回元素满足谓词的最长前缀


public static <A>List<A> takeWhile(List<A> list, Predicate<A> predicate){
        int i = 0;
        for (A item : list) {

//            检查列表中的当前//项目是否满足谓词
            if (!predicate.test(item)){

//                如果不满足,返回该//项目之前的前缀子//列表
                return list.subList(0,i);
            }
            i++;
        }

//        列表中的所有项目//都满足谓词,因此返//回列表本身
        return list;
    }

优化:

//    利用这个方法,你就可以优化 isPrime 方法,只用不大于被测数平方根的质数去测试了

    public static boolean isPrime(List<Integer> primes, int candidate){
        int candidateRoot = (int) Math.sqrt((double) candidate);
        return takeWhile(primes,i->i<=candidateRoot)
                .stream()
                .noneMatch(p-> candidate % p == 0);
    }

有了这个新的 isPrime 方法,你就可以实现自己的自定义收集器了。首先要声明一个实现 Collector 接口的新类,然后要开发 Collector 接口所需的五个方法

  • 第一步:定义 Collector 类的签名

Collector 接口的定义是

public interface Collector<T, A, R>

T 、 A 和 R 分别是

  • 流中元素的类型

  • 用于累积部分结果的对象类型

  • collect 操作最终结果的类型

这里应该收集 Integer 流,而累加器和结果类型则都是 Map<Boolean,List>

键是 true 和 false ,值则分别是质数和非质数的 List

最终实现:


public class PrimeNumbersCollector implements Collector<Integer, Map<Boolean, List<Integer>>,Map<Boolean, List<Integer>>> {


//    归约过程
    @Override
    public Supplier<Map<Boolean, List<Integer>>> supplier() {
        return ()-> new HashMap<Boolean,List<Integer>>(){{
            put(true,new ArrayList<Integer>());
            put(false,new ArrayList<Integer>());
        }

        };

//        这里不但创建了用作累加器的 Map ,还为 true 和 false 两个键下面初始化了对应的空列表。
//在收集过程中会把质数和非质数分别添加到这里
    }

    @Override
    public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
        return (Map<Boolean,List<Integer>> acc,Integer candidate) ->
        {
//            根据 isPrime 的//结果,获取质数//或非质数列表
            acc.get(M1.isPrime(acc.get(true),candidate))

//                    将被测数添加到//相应的列表中
                    .add(candidate);
        };
    }


//    在并行收集时把两个部分累加器合并起来,这里,它只需要合并两个 Map ,即
//将第二个 Map 中质数和非质数列表中的所有数字合并到第一个 Map 的对应列表中就行了
    @Override
    public BinaryOperator<Map<Boolean, List<Integer>>> combiner() {
        return (Map<Boolean, List<Integer>> map1,
                Map<Boolean, List<Integer>> map2) ->
        {
            map1.get(true).addAll(map2.get(true));
            map1.get(false).addAll(map2.get(false));
            return map1;
        };
    }

    @Override
    public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
    }
}

测试:


public class M2 {


    public static Map<Boolean, List<Integer>>
    partitionPrimesWithCustomCollector(int n) {
        return IntStream.rangeClosed(2, n).boxed()
                .collect(new PrimeNumbersCollector());
    }


    public static void main(String[] args) {

        System.out.println(partitionPrimesWithCustomCollector(500));

    }
}


VN1630A硬件配置指南:手把手教你用CanApe搭建XCP标定工程(附ELF文件配置技巧) 本文详细介绍了如何使用VN1630A硬件和CanApe软件搭建XCP标定工程,包括硬件配置、CanApe工程创建、ELF文件处理及XCP协议层配置等关键步骤。特别针对实际项目中常见的错误和细节问题提供解决方案,帮助工程师高效完成ECU参数标定工作。 阅读详情

相关推荐

【Agora 本地录制最全总结- 基于Java SDK】

Agora 本地服务端录制

lizhengze1117的博客 1790

Java8 函数式编程 笔记-收集器基本用法

   参考并建议阅读《Java8函数式编程》 /《Java8 实战》

YF067的博客 509

axi时序图_(AXI)握手协议(pvld/prdy或者valid-ready)中ready打拍技巧

内容提要ready打拍的问题用FIFO的思路去解决用Buffer的思路去解决问题提出:ready时序如何优化?在valid/ready 握手协议中,valid 与 data的时序优化比较容易理解,(不熟悉valid/ready协议或者valid打拍方法的)大家可以参考上次推送(握手协议(pvld/prdy或者valid-ready或AXI)中Valid及data打拍技巧)。 但是有时候,关键路径是...

weixin_29763641的博客 3319

java8特性

个人总结: lamdba表达式: 参考网上: http://www.cnblogs.com/franson-2016/p/5593080.html http://blog.youkuaiyun.com/ioriogami/article/details/12782141/ 简单例子: // 1. 不需要参数,返回值为 5 () -> 5 // 2. 接收一个参数(数字类型),返

lianbancai的博客 256

Human3.6M 数据集介绍及下载

360 万张 3D 人体姿势和对应图像11 名专业演员(6 男 5 女)7 个场景(讨论、吸烟、拍照以及打电话等等)

XavierJ的博客 3万+

MIC:最大信息系数

目录 1. 概念 1.1 MIC 1.2 互信息 2.MIC的优点 3. 算法原理 3.1 MIC公式原理 3.2MIC计算步骤 (1)计算最大互信息值 (2)对最大的互信息值进行归一化 (3)选择不同尺度下互信息的最大值作为MIC值 4. 代码实现 6. 其他方法 1. 概念 1.1 MIC MIC,即(Maximal Information...

满腹的小不甘 5万+

关于Java8的精心总结

前言 ​ 最近公司里比较新的项目里面,看到了很多关于java8新特性的用法,由于之前自己对java8的新特性不是很了解也没有去做深入研究,所以最近就系统的去学习了一下,然后总结了一篇文章第一时间和大家分享一下。 ​ ​ 在了解一项新技术之前,我们需要了解我们为什么要去学习它以及它的优点,以下是我总结的: Java8(又称jdk1.8)是java语言开发的一个主要版本,Java8是oracal公司于2014年3月发布,可以看成是自java5以来最具有革命性的版本。

码到成功 918

Python 神经计算实训 实训答案分享

【代码】Python 神经计算实训 实训答案分享。

2403_89314916的博客 343

java final 实例_Java中final实现原理的深入分析(附示例)

本篇文章给大家带来的内容是关于Java中final实现原理的深入分析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。一、final变量final成员变量表示常量,只能被...

weixin_39746229的博客 505

物料分类账-返工工单(自身)差异分摊逻辑

如上图所示,当月入库的全部消耗,而返工消耗的数量为100PCS,所以返工消耗占总消耗的比例为100pcs/(205pcs+100pcs)=0.327868852。备注:红色箭头的物料号为产品自己本身所产生的价差,就是返工工单所承担的差异,而我们要计算的就是这个值,所以计算价差不会包括此项。价差合计就是不包括初始分摊的价差9153.7,剩下的价差合计就是返工工单自身所承担的差异4465.21。每一次返工工单所承担的差异都会按照价差*承担比例去计算承担的差异,知道承担完所有差异。

Yeol827的博客 2485

GVINS数据采集(u-blox fp9 + D435i)

1、配置千寻的RTK账号 采集数据当天购买千寻的RTK账号,然后在Window下,输入账号和密码 (具体的步骤如之前面连接所示),查看u-center软件上的信息,确保接收机可以收到RTK信息; 2、查看Linux系统下u-blox是否可以正常接受GPS信号和RTK数据流 1、GPS信号信息 // 打开端口 1 cd ublox_f9p/src // 或者在终端1下也可以 source devel/setup.bash r.....................

努力努力努力 2609

java8 常用操作及解释

java8常用操作

weixin_41987908的博客 405

Android实现自动输入文本效果 (附完整源码)

Android实现自动输入文本效果 (附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 413

java8,stream笔记2

List<Employee> emps = Arrays.asList( new Employee(102, "李四", 79, 6666.66, Employee.Status.BUSY), new Employee(101, "张三", 18, 9999.99, Employee.Status.FREE), ...

sinat_36572927的博客 115

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Java8学习笔记(2)

JAVA8学习笔记

baisk2117的博客 252

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

java8笔记2

//并行流计算1-100的和 Integer sum = IntStream.rangeClosed(1, 100).boxed().parallel().collect(Collectors.summingInt(n -> n)); System.out.println(sum); //第二种写法 Integer twoSum = Stream.iterate(1, n -> n + 1).limit(100).paralle..

ldlly0505的博客 177

Java Web 笔记记录分享网站系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

直接拿走,意外获得200多套代码,需要的滴我Java Web 笔记记录分享网站系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】(可提供说明文档(通过。👨‍🎓博主简介 ❤计算机在读硕士 | 优快云 专业博客 | Java 技术布道者 ❤深耕实验室一线,痴迷 SpringBoot。请dd我获取更详细的演示视频 或者直接加我,网名和签名。

QQ1273148749的博客 206

Java后端笔记8-集合2

集合 List接口 import java.util.ArrayList; import java.util.LinkedList; /** * Create by xiye on 2019/11/27 15:15 */ public class Demo1_List子类 { /*LIst * ArrayList集合:其数据存储结构是数组结构,元素增删慢,查找快 ...

XIYECODE的博客 195

Java Web 学生读书笔记共享平台系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

东南大学计算机科学与技术专业在读研究生,优快云博客专家,资深Java技术实践者。在校期间深度参与实验室前沿项目研发,现为优快云特邀作者及掘金优质创作者,致力于推动技术知识的传播与分享。优快云上做毕设辅导的都是专业技术服务,大家都要生活,这个很正常。我和其他人不同的是,我有自己的项目库存,不需要找别人拿货再加价,所以能给到超低价格。在学术项目工程化实现方面拥有丰富经验,擅长将理论知识转化为可落地的技术方案。遇见即是缘,欢迎交流,你别地能找到的源码我都有!可以直接联系我查看详细视频,个性签名!

2501_92839909的博客 75

Java8学习笔记(2) -- InvokeDynamic指令

详细分析invokedynamic指令

zxh的专栏 1万+

Java8学习笔记:2.Stream流

今天学习总结了Stream(),在此备注: 惰性求值方法:类似sql查询的where条件,过滤stream的条件。如:filter() 及早求值函数:类似sql查询的seletct条件,直返结果。如:count(),max() 方法总结: count():及早求值函数,求总数。 collect():由Stream里的值生成一个列表,是一个及早求值操作(.of()方法时惰性求值的方法)

bai020的博客 1309

java中8<<2_8.2(java学习笔记)反射

一、基础知识对象是表示或封装一些数据,一个类被加载后JVM会创建一个对应该类的Class对象,类的整个结构信息会被放在对应的对象中,通过这个对象我们可以获取改类的全部信息,而这些操作称为反射。二、反射基本操作2.1获取对象类上面说了每一个类在加载时会创建一个对应该类的Class对象,这个对象中存放着这个类相对应的信息。我们通过反射可以对这个类进行操作。那么首先我们要获取这个类对应的Class对象,...

weixin_34665386的博客 427

Java高级(8.2笔记)

File

mjy2253308284的博客 581

Java8实战笔记2 lambda以及方法引用

简要介绍lambda以及方法引用、构造函数引用的复杂方法

delete_you的博客 1087

《Java JDK8学习笔记》读书笔记(2)

第二章重点讲述JDK相关和IDE相关,如果已经熟悉IDE使用,或需要使用别的IDE(如Eclipse)的童鞋可以不用看这部分。 关于JDK部分,讲的相对较细,希望大家重点学习,相信一定有收获的。

默然说话 2921
上一篇: Java8笔记(1)
下一篇: Java8笔记(3)
Coder_py
博客等级 码龄10年 106粉丝 258原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值