三. Stream API

news2024/10/7 16:15:53

1. 过滤

record Fruit(String cname, String name, String category, String color) { }

Stream.of(
    new Fruit("草莓", "Strawberry", "浆果", "红色"),
    new Fruit("桑葚", "Mulberry", "浆果", "紫色"),
    new Fruit("杨梅", "Waxberry", "浆果", "红色"),
    new Fruit("核桃", "Walnut", "坚果", "棕色"),
    new Fruit("草莓", "Peanut", "坚果", "棕色"),
    new Fruit("蓝莓", "Blueberry", "浆果", "蓝色")
)

在这里插入图片描述
找到所有浆果

.filter(f -> f.category.equals("浆果"))

找到蓝色的浆果

方法1:

.filter(f -> f.category().equals("浆果") && f.color().equals("蓝色"))

方法2:让每个 lambda 只做一件事,两次 filter 相对于并且关系

.filter(f -> f.category.equals("浆果"))
.filter(f -> f.color().equals("蓝色"))

方法3:让每个 lambda 只做一件事,不过比方法2强的地方可以 or,and,nagate 运算

.filter(((Predicate<Fruit>) f -> f.category.equals("浆果")).and(f -> f.color().equals("蓝色")))

2. 映射

在这里插入图片描述

.map(f -> f.cname() + "酱")

3. 降维(扁平化)

例1
在这里插入图片描述

Stream.of(
        List.of(
                new Fruit("草莓", "Strawberry", "浆果", "红色"),
                new Fruit("桑葚", "Mulberry", "浆果", "紫色"),
                new Fruit("杨梅", "Waxberry", "浆果", "红色"),
                new Fruit("蓝莓", "Blueberry", "浆果", "蓝色")
        ),
        List.of(
                new Fruit("核桃", "Walnut", "坚果", "棕色"),
                new Fruit("草莓", "Peanut", "坚果", "棕色")
        )
)
    
.flatMap(Collection::stream)    
  • 这样把坚果和浆果两个集合变成了含六个元素的水果流

例2:

Stream.of(
        new Order(1, List.of(
                new Item(6499, 1, "HUAWEI MateBook 14s"),
                new Item(6999, 1, "HUAWEI Mate 60 Pro"),
                new Item(1488, 1, "HUAWEI WATCH GT 4")
        )),
        new Order(1, List.of(
                new Item(8999, 1, "Apple MacBook Air 13"),
                new Item(7999, 1, "Apple iPhone 15 Pro"),
                new Item(2999, 1, "Apple Watch Series 9")
        ))
)

想逐一处理每个订单中的商品

.flatMap(order -> order.items().stream())

这样把一个有两个元素的订单流,变成了一个有六个元素的商品流

4. 构建

在这里插入图片描述

根据已有的数组构建流

Arrays.stream(array)

根据已有的 Collection 构建流(包括 List,Set 等)

List.of("a","b","c").stream()

把一个对象变成流

Stream.of("d")

把多个对象变成流

Stream.of("x", "y")

5. 拼接

两个流拼接

Stream.concat(Stream.of("a","b","c"), Stream.of("d"))

6. 截取

Stream.concat(Stream.of("a", "b", "c"), Stream.of("d"))
    .skip(1)
    .limit(2)
  • skip 是跳过几个元素

  • limit 是限制处理的元素个数

  • dropWhile 是 drop 流中元素,直到条件不成立,留下剩余元素

  • takeWhile 是 take 流中元素,直到条件不成立,舍弃剩余元素

        Stream<Integer> concat = Stream.concat(s1, s2);
        concat.takeWhile(x->x<3).forEach(System.out::print);
        concat.dropWhile(x->x<3).forEach(System.out::print);

7. 生成

在这里插入图片描述

生成从 0 ~ 9 的数字

IntStream.range(0, 10)

或者

IntStream.rangeClosed(0, 9)

如果想订制,可以用 iterate 方法,例如下面生成奇数序列

IntStream.iterate(1, x -> x + 2)
  • 参数1 是初始值
  • 参数2 是一个特殊 Function,即参数类型与返回值相同,它会根据上一个元素 x 的值计算出当前元素
  • 需要用 limit 限制元素个数

也可以用 iterate 的重载方法

IntStream.iterate(1, x -> x < 10, x -> x + 2)
  • 参数1 是初始值
  • 参数2 用来限制元素个数,一旦不满足此条件,流就结束
  • 参数3 相当于上个方法的参数2

iterate 的特点是根据上一个元素计算当前元素,如果不需要依赖上一个元素,可以改用 generate 方法

例如下面是生成 5 个随机 int

Stream.generate(()-> ThreadLocalRandom.current().nextInt()).limit(5)

不过如果只是生成随机数的话,有更简单的办法

ThreadLocalRandom.current().ints(5)

如果要指定上下限,例如下面是生成从 0~9 的100个随机数

ThreadLocalRandom.current().ints(100, 0, 10)

8. 查找与判断

在这里插入图片描述

下面的代码找到流中任意(Any)一个偶数

int[] array = {1, 3, 5, 4, 7, 6, 9};

Arrays.stream(array)
    .filter(x -> (x & 1) == 0)
    .findAny()
    .ifPresent(System.out::println);
  • 注意 findAny 返回的是 OptionalInt 对象,因为可能流中不存在偶数
  • 对于 OptionalInt 对象,一般需要用 ifPresent(findAny筛选后存在数据即处理,否则不处理) 或 orElse(提供默认值)来处理

与 findAny 比较类似的是 firstFirst,它俩的区别

  • findAny 是找在流中任意位置的元素,不需要考虑顺序,对于上例返回 6 也是可以的
  • findFirst 是找第一个出现在元素,需要考虑顺序,对于上例只能返回 4
  • findAny 在顺序流中与 findFirst 表现相同,区别在于并行流下会更快

判断流中是否存在任意一个偶数

Arrays.stream(array).anyMatch(x -> (x & 1) == 0)
  • 它返回的是 boolean 值,可以直接用来判断

判断流是否全部是偶数

Arrays.stream(array).allMatch(x -> (x & 1) == 0)
  • 同样,它返回的是 boolean 值,可以直接用来判断

判断流是否全部不是偶数

Arrays.stream(array).noneMatch(x -> (x & 1) == 0)
  • noneMatch 与 allMatch 含义恰好相反

9. 排序与去重

已知有数据

record Hero(String name, int strength) { }

Stream.of(
    new Hero("独孤求败", 100),
    new Hero("令狐冲", 90),
    new Hero("风清扬", 98),
    new Hero("东方不败", 98),
    new Hero("方证", 92),
    new Hero("任我行", 92),
    new Hero("冲虚", 90),
    new Hero("向问天", 88),
    new Hero("不戒", 88)
)

要求,首先按 strength 武力排序(逆序),武力相同的,按姓名长度排序(正序)

仅用 lambda 来解

.sorted((a,b)-> {
    int res = Integer.compare(b.strength(), a.strength());
    return (res == 0) ? Integer.compare(a.nameLength(), b.nameLength()) : res; 
})

方法引用改写

.sorted(
    Comparator.comparingInt(Hero::strength)
      .reversed()
      .thenComparingInt(Hero::nameLength)
)

其中:

  • comparingInt 接收一个 key 提取器(说明按对象中哪部分来比较),返回一个比较器
  • reversed 返回一个顺序相反的比较器
  • thenComparingInt 接收一个 key 提取器,返回一个新比较器,新比较器在原有比较器结果相等时执行新的比较逻辑

增加一个辅助方法

record Hero(String name, int strength) {
    int nameLength() {
        return this.name.length();
    }
}

原理:

.sorted((e, f) -> {
    int res =
        ((Comparator<Hero>) (c, d) ->
            ((Comparator<Hero>) (a, b) -> Integer.compare(a.strength(), b.strength()))
                .compare(d, c))
            .compare(e, f);
    return (res == 0) ? Integer.compare(e.nameLength(), f.nameLength()) : res;
})

如果不好看,改成下面的代码

.sorted(step3(step2(step1())))

static Comparator<Hero> step1() {
    return (a, b) -> Integer.compare(a.strength(), b.strength());
}

static Comparator<Hero> step2(Comparator<Hero> step1) {
    return (c, d) -> step1.compare(d, c);
}

static Comparator<Hero> step3(Comparator<Hero> step2) {
    return (e, f) -> {
        int res = step2.compare(e, f);
        return (res == 0) ? Integer.compare(e.nameLength(), f.nameLength()) : res;
    };
}

10. 化简

reduce(init, (p,x) -> r)

  • init 代表初始值
  • (p,x) -> r 是一个 BinaryOperator,作用是根据上次化简结果 p 和当前元素 x,得到本次化简结果 r

这样两两化简,可以将流中的所有元素合并成一个结果

/*
    化简:两两合并,只剩一个
    适合:最大值、最小值、求和、求个数...
        .reduce((p, x) -> r)        p 上次的合并结果, x 当前元素, r 本次合并结果
        .reduce(init, (p, x) -> r)
        .reduce(init, (p, x) -> r, (r1, r2) -> r)
 */
public class C10ReduceTest {
    record Hero(String name, int strength) {

    }

    public static void main(String[] args) {
        Stream<Hero> stream = Stream.of(
                new Hero("令狐冲", 90),
                new Hero("风清扬", 98),
                new Hero("独孤求败", 100),
                new Hero("方证", 92),
                new Hero("东方不败", 98),
                new Hero("冲虚", 90),
                new Hero("向问天", 88),
                new Hero("任我行", 92),
                new Hero("不戒", 88)
        );

        // 1) 求武力最高的 hero
//        Optional<Hero> result = stream.reduce((h1, h2) -> h1.strength() > h2.strength() ? h1 : h2);
//        Hero result = stream.reduce(new Hero("-", -1), (h1, h2) -> h1.strength() > h2.strength() ? h1 : h2);
//        System.out.println(result);

        // 2) 求高手总数
//        System.out.println(stream.map(h -> 1).reduce(0, (a, b) -> a + b));

//        System.out.println(stream.count());
//        System.out.println(stream.max(Comparator.comparingInt(Hero::strength)));
//        System.out.println(stream.min(Comparator.comparingInt(Hero::strength)));
//        System.out.println(stream.mapToInt(Hero::strength).sum());
        System.out.println(stream.mapToInt(Hero::strength).average());
    }
}

11. 收集

collect( supplier, accumulator, combiner)

  • supplier 是描述如何创建收集容器 c :()-> c
  • accumulator 是描述如何向容器 c 添加元素 x:(c, x) -> void
  • combiner 是描述如何合并两个容器:(c1, c2) -> void
    • 串行流下不需要合并容器
    • 并行流如果用的是并发容器,也不需要合并
public class C11CollectTest {
    record Hero(String name, int strength) {

    }

    /*
        收集:将元素收集入容器
            .collect(() -> c, (c, x) -> void, ?)

            () -> c             创建容器 c
            (c, x) -> void      将元素 x 加入 容器 c
     */
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("令狐冲", "风清扬", "独孤求败", "方证",
                "东方不败", "冲虚", "向问天", "任我行", "不戒", "不戒", "不戒", "不戒");
        /*
            1) 收集到 List
            List<String> result = stream.collect(() -> new ArrayList<>(), (list, x) -> list.add(x), (a, b) -> { });
            ArrayList::new   ()->new ArrayList()
            ArrayList::add   (list,x)->list.add(x)
            List<String> result = stream.collect(ArrayList::new, ArrayList::add, (a, b) -> { });
         */

        /*
            2) 收集到 Set
            Set<String> result = stream.collect(LinkedHashSet::new, Set::add, (a, b) -> { });
         */

        /*
            3)收集到 Map
            Map<String, Integer> result = stream.collect(HashMap::new, (map,x)->map.put(x, 1), (a, b) -> { });
         */

//        StringBuilder sb = stream.collect(StringBuilder::new, StringBuilder::append, (a,b)->{});
//        System.out.println(sb);

        StringJoiner sb = stream.collect(()->new StringJoiner(","), StringJoiner::add, (a,b)->{});
        System.out.println(sb);
    }
}

12. 收集器

Collectors 类 中提供了很多现成的收集器,详情见网页

// Collector 收集器
public class C12CollectorTest {
    record Hero(String name, int strength) {

    }

    public static void main(String[] args) {
        Stream<String> stream = Stream.of("令狐冲", "风清扬", "独孤求败", "方证",
                "东方不败", "冲虚", "向问天", "任我行", "不戒");
        /*
            1) 收集到 List
            List<String> result = stream.collect(() -> new ArrayList<>(), (list, x) -> list.add(x), (a, b) -> { });
            ArrayList::new   ()->new ArrayList()
            ArrayList::add   (list,x)->list.add(x)
            List<String> result = stream.collect(ArrayList::new, ArrayList::add, (a, b) -> { });
         */
//        List<String> result = stream.collect(Collectors.toList());

        /*
            2) 收集到 Set
            Set<String> result = stream.collect(LinkedHashSet::new, Set::add, (a, b) -> { });
         */
//        Set<String> result = stream.collect(Collectors.toSet());

        /*
            3)收集到 StringBuilder
            StringBuilder sb = stream.collect(StringBuilder::new, StringBuilder::append, (a,b)->{});
         */
//        String result = stream.collect(Collectors.joining());

        /*
            4)收集到 StringJoiner
            StringJoiner sb = stream.collect(()->new StringJoiner(","), StringJoiner::add, (a,b)->{});
         */
//        String result = stream.collect(Collectors.joining(","));

        /*
            3)收集到 Map
            Map<String, Integer> result = stream.collect(HashMap::new, (map,x)->map.put(x, 1), (a, b) -> { });
         */
//        Map<String, Integer> map = stream.collect(Collectors.toMap(x -> x, x -> 1));

        /*
            Map
            3: new ArrayList(["令狐冲","风清扬","向问天","任我行"])
            4: new ArrayList(["独孤求败","东方不败"])
            2: new ArrayList(["方证","冲虚","不戒"])

            下游收集器
         */

//        Map<Integer, List<String>> result = stream.collect(Collectors.groupingBy(x -> x.length(), Collectors.toList()));

        Map<Integer, String> result = stream.collect(Collectors.groupingBy(x -> x.length(), Collectors.joining(",")));
        for (Map.Entry<Integer, String> e : result.entrySet()) {
            System.out.println(e);
        }
    }
}

13. 下游收集器

做 groupingBy 分组收集时,组内可能需要进一步的数据收集,称为下游收集器,详情见网页

public class C13GroupingByTest {
    record Hero(String name, int strength) {

    }

    public static void main(String[] args) {
        Stream<Hero> stream = Stream.of(
                new Hero("令狐冲", 90),
                new Hero("风清扬", 98),
                new Hero("独孤求败", 100),
                new Hero("方证", 92),
                new Hero("东方不败", 98),
                new Hero("冲虚", 90),
                new Hero("向问天", 88),
                new Hero("任我行", 92),
                new Hero("不戒", 88)
        );

        /*
         1. mapping(x->y, dc)  需求:根据名字长度分组,分组后组内只保留他们的武力值
             new Hero("令狐冲", 90)->90
             dc 下游收集器 down collector

             stream.collect(groupingBy(h -> h.name().length(), mapping(h -> h.strength(), toList())));
         */

        /*
         2. filtering(x->boolean, dc)  需求:根据名字长度分组,分组后组内过滤掉武力小于 90 的

         在分组收集的过程中,执行过滤
         stream.collect(groupingBy(h -> h.name().length(), filtering(h -> h.strength() >= 90, toList())));
         先过滤,再来分组收集
         stream.filter(h -> h.strength() >= 90).collect(groupingBy(h -> h.name().length(), toList()));
         */

        /*
         3. flatMapping(x->substream, dc)     需求:根据名字长度分组,分组后组内保留人名,并且人名切分成单个字符

         "令狐冲".chars().mapToObj(Character::toString).forEach(System.out::println);

         stream.collect(groupingBy(h -> h.name().length(),
                flatMapping(h -> h.name().chars().mapToObj(Character::toString), toList())));
         */

        /*
         4. counting() 需求:根据名字长度分组,分组后求每组个数

         stream.collect(groupingBy(h -> h.name().length(), counting()));
         */

        /*
          5. minBy((a,b)->int) 需求:根据名字长度分组,分组后求每组武功最低的人
          6. maxBy((a,b)->int) 需求:根据名字长度分组,分组后求每组武功最高的人

          stream.collect(groupingBy(h -> h.name().length(), maxBy(Comparator.comparingInt(Hero::strength))));
         */

        /*
          7. summingInt(x->int)            需求:根据名字长度分组,分组后求每组武力和
          8. averagingDouble(x->double)    需求:根据名字长度分组,分组后求每组武力平均值

          stream.collect(groupingBy(h -> h.name().length(), averagingDouble(h -> h.strength())));
         */

        /*
         9. reducing(init,(p,x)->r)

         求和
          stream.collect(groupingBy(h -> h.name().length(), mapping(h -> h.strength(), reducing(0, (p, x) -> p + x))));
         求个数
          stream.collect(groupingBy(h -> h.name().length(), mapping(h -> 1, reducing(0, (p, x) -> p + x))));
        */

        // 求平均,缺少 finisher
        Map<Integer, double[]> collect = stream.collect(groupingBy(h -> h.name().length(),
                mapping(h -> new double[]{h.strength(), 1},
                        reducing(new double[]{0, 0}, (p, x) -> new double[]{p[0] + x[0], p[1] + x[1]}))));
        for (Map.Entry<Integer, double[]> e : collect.entrySet()) {
            System.out.println(e.getKey() + ":" + Arrays.toString(e.getValue()));
        }
    }
}

14. 基本流

在这里插入图片描述在这里插入图片描述

基本类型流指 IntStream、LongStream 和 DoubleStream,它们在做数值计算时有更好的性能。

转换成基本流

  • mapToInt
  • mapToLong
  • mapToDouble
  • flatMapToInt
  • flatMapToLong
  • flatMapToDouble
  • mapMultiToInt
  • mapMultiToLong
  • mapMultiToDouble

基本流转对象流

  • mapToObj
  • boxed
public class C14Effective {
    record Hero(String name, int strength) {

    }
    /*
        三种基本流
     */
    public static void main(String[] args) {
        IntStream a = IntStream.of(97, 98, 99);
        LongStream b = LongStream.of(1L, 2L, 3L);
        DoubleStream c = DoubleStream.of(1.0, 2.0, 3.0);

        Stream<Integer> d = Stream.of(1, 2, 3);

//        a.mapToObj(Character::toString).forEach(System.out::println);

//        IntSummaryStatistics stat = a.summaryStatistics();
//        System.out.println(stat.getSum());
//        System.out.println(stat.getCount());
//        System.out.println(stat.getMax());
//        System.out.println(stat.getMin());
//        System.out.println(stat.getAverage());

        Stream<Hero> stream = Stream.of(
                new Hero("令狐冲", 90),
                new Hero("风清扬", 98)
        );

        stream.mapToInt(Hero::strength).forEach(System.out::println );
    }
}

15. 特性

  1. 一次使用:流只能使用一次(终结方法只能调用一次)
  2. 两类操作:
    1. 中间操作,lazy 懒惰的
    2. 终结操作,eager 迫切的
public class C15Summary {
    public static void main(String[] args) {
        /*
           掌握 Stream 流的特性

            1. 一次使用
            2. 两类操作(中间操作 lazy 懒惰, 终结操作 eager 迫切)

         */
        Stream<Integer> s1 = Stream.of(1, 3, 5); // 水滴
        // -----------------------------------    -------------------------  阀门
        s1
                .map(x -> x + 1)                        // 水管
                .filter(x -> x <= 5)                    // 水管
                .forEach(x -> System.out.println(x));   // 水管 总阀门
    }
}

《stream.html》

16. 并行

// 并行流
public class C16Parallel {
    public static void main(String[] args) {

        /*List<Integer> collect = Stream.of(1, 2, 3, 4)
                .collect(Collector.of(
                        () -> {
                            System.out.printf("%-12s %s%n",simple(),"create");
                            return new ArrayList<Integer>();
                        },
                        (list, x) -> {
                            List<Integer> old = new ArrayList<>(list);
                            list.add(x);
                            System.out.printf("%-12s %s.add(%d)=>%s%n",simple(), old, x, list);
                        },
                        (list1, list2) -> {
                            List<Integer> old = new ArrayList<>(list1);
                            list1.addAll(list2);
                            System.out.printf("%-12s %s.add(%s)=>%s%n", simple(),old, list2, list1);
                            return list1;
                        },
                        list -> null,
                        Collector.Characteristics.IDENTITY_FINISH
                ));

                */


        /*
            1) 数据量问题: 数据量大时才建议用并行流
            2) 线程会无限增加吗: 跟 cpu 能处理的线程数相关
            3) 收尾的意义: 转不可变集合、StringBuilder 转 String ...
            4) 是否线程安全: 不会有线程安全问题
            5) 特性:
                是否需要收尾(默认收尾)
                是否需要保证顺序(默认保证)
                容器是否支持并发(默认不需要支持)

                到达选择哪一种?
                    A. Characteristics.CONCURRENT + Characteristics.UNORDERED + 线程安全容器  并发量大性能可能会受影响
                    B. 默认 + 线程不安全容器                                                   占用内存多,合并多也会影响性能

         */
        List<Integer> collect = Stream.of(1, 2, 3, 4)
                .parallel()
                .collect(Collector.of(
                        () -> {
                            System.out.printf("%-12s %s%n", simple(), "create");
                            return new Vector<Integer>();
                        },                                                                          // 1.如何创建容器
                        (list, x) -> {
                            List<Integer> old = new ArrayList<>(list);
                            list.add(x);
                            System.out.printf("%-12s %s.add(%d)=>%s%n", simple(), old, x, list);
                        },                                                                          // 2.如何向容器添加数据
                        (list1, list2) -> {
                            List<Integer> old = new ArrayList<>(list1);
                            list1.addAll(list2);
                            System.out.printf("%-12s %s.add(%s)=>%s%n", simple(), old, list2, list1);
                            return list1;
                        },                                                                          // 3.如何合并两个容器的数据
                        list -> {
                            System.out.printf("%-12s finish: %s=>%s%n", simple(), list, list);
                            return Collections.unmodifiableList(list);
                        }                                                                           // 4.收尾
                        , Collector.Characteristics.IDENTITY_FINISH                                 // 不需要收尾
                        , Collector.Characteristics.UNORDERED                                       // 不需要保证顺序
                        , Collector.Characteristics.CONCURRENT                                      // 容器需要支持并发
                ));

        System.out.println(collect);
        collect.add(100);
        System.out.println(collect);
    }

    private static String simple() {
        String name = Thread.currentThread().getName();
        int idx = name.indexOf("worker");
        if (idx > 0) {
            return name.substring(idx);
        }
        return name;
    }
}

17. 效率

17.1 数组求和

其中

  • primitive 用 loop 循环对 int 求和
  • intStream 用 IntStream 对 int 求和
  • boxed 用 loop 循环对 Integer 求和
  • stream 用 Stream 对 Integer 求和

元素个数 100

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
T01Sum.primitiveavgt525.424± 0.782ns/op
T01Sum.intStreamavgt547.482± 1.145ns/op
T01Sum.boxedavgt572.457± 4.136ns/op
T01Sum.streamavgt5465.141± 4.891ns/op

元素个数 1000

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
T01Sum.primitiveavgt5270.556± 1.277ns/op
T01Sum.intStreamavgt5292.467± 10.987ns/op
T01Sum.boxedavgt5583.929± 57.338ns/op
T01Sum.streamavgt55948.294± 2209.211ns/op

元素个数 10000

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
T01Sum.primitiveavgt52681.651± 12.614ns/op
T01Sum.intStreamavgt52718.408± 52.418ns/op
T01Sum.boxedavgt56391.285± 358.154ns/op
T01Sum.streamavgt544414.884± 3213.055ns/op

结论:

  • 做数值计算,优先挑选基本流(IntStream 等)在数据量较大时,它的性能已经非常接近普通 for 循环
  • 做数值计算,应当避免普通流(Stream)性能与其它几种相比,慢一个数量级
// 性能:求和
public class T01Sum {
    @State(Scope.Benchmark)
    public static class MyState {
        public static final int COUNT = 10000;

        public int[] numbers = new int[COUNT];
        public List<Integer> numberList = new ArrayList<>(COUNT);

        public MyState() {
            for (int i = 0; i < COUNT; i++) {
                int x = i + 1;
                numbers[i] = x;
                numberList.add(i, x);
            }
        }
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public int primitive(MyState state) {
        int sum = 0;
        for (int number : state.numbers) {
            sum += number;
        }
        return sum;
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public int boxed(MyState state) {
        int sum = 0;
        for (Integer i : state.numberList) {
            sum += i;
        }
        return sum;
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public int stream(MyState state) {
        return state.numberList.stream().reduce(0, (a, b) -> a + b);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public int intStream(MyState state) {
        return IntStream.of(state.numbers).sum();
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(T01Sum.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();

//        MyState state = new MyState();
//        T01Sum test = new T01Sum();
//        System.out.println(test.primitive(state));
//        System.out.println(test.boxed(state));
//        System.out.println(test.stream(state));
//        System.out.println(test.intStream(state));
    }
}

17.2 求最大值

其中(原始数据都是 int,没有包装类)

  • custom 自定义多线程并行求最大值
  • parallel 并行流求最大值
  • sequence 串行流求最大值
  • primitive loop 循环求最大值

元素个数 100

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
T02Parallel.customavgt539619.796± 1263.036ns/op
T02Parallel.parallelavgt56754.239± 79.894ns/op
T02Parallel.primitiveavgt529.538± 3.056ns/op
T02Parallel.sequenceavgt580.170± 1.940ns/op

元素个数 10000

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
T02Parallel.customavgt541656.093± 1537.237ns/op
T02Parallel.parallelavgt511218.573± 1994.863ns/op
T02Parallel.primitiveavgt52217.562± 80.981ns/op
T02Parallel.sequenceavgt55682.482± 264.645ns/op

元素个数 1000000

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
T02Parallel.customavgt5194984.564± 25794.484ns/op
T02Parallel.parallelavgt5298940.794± 31944.959ns/op
T02Parallel.primitiveavgt5325178.873± 81314.981ns/op
T02Parallel.sequenceavgt5618274.062± 5867.812ns/op

结论:

  • 并行流相对自己用多线程实现分而治之更简洁
  • 并行流只有在数据量非常大时,才能充分发力,数据量少,还不如用串行流

17.3 并行(发)收集

元素个数 100

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
loop1avgt51312.389± 90.683ns/op
loop2avgt51776.391± 255.271ns/op
sequenceavgt51727.739± 28.821ns/op
parallelNoConcurrentavgt527654.004± 496.970ns/op
parallelConcurrentavgt516320.113± 344.766ns/op

元素个数 10000

BenchmarkModeCntScore (ns/op)Error (ns/op)Units
loop1avgt5211526.546± 13549.703ns/op
loop2avgt5203794.146± 3525.972ns/op
sequenceavgt5237688.651± 7593.483ns/op
parallelNoConcurrentavgt5527203.976± 3496.107ns/op
parallelConcurrentavgt5369630.728± 20549.731ns/op

元素个数 1000000

BenchmarkModeCntScore (ms/op)Error (ms/op)Units
loop1avgt569.154± 3.456ms/op
loop2avgt583.815± 2.307ms/op
sequenceavgt5103.585± 0.834ns/op
parallelNoConcurrentavgt5167.032± 15.406ms/op
parallelConcurrentavgt552.326± 1.501ms/op

结论:

  • sequence 是一个容器单线程收集,数据量少时性能占优
  • parallelNoConcurrent 是多个容器多线程并行收集,时间应该花费在合并容器上,性能最差
  • parallelConcurrent 是一个容器多线程并发收集,在数据量大时性能较优

17.4 MethodHandle 性能

正常方法调用、反射、MethodHandle、Lambda 的性能对比

BenchmarkModeCntScoreErrorUnits
Sample2.lambdathrpt5389307532.881± 332213073.039ops/s
Sample2.methodthrpt5157556577.611± 4048306.620ops/s
Sample2.originthrpt5413287866.949± 65182730.966ops/s
Sample2.reflectionthrpt591640751.456± 37969233.369ops/s

18. 综合练习

  1. 将 filter 的课堂例题修改为方法引用方式实现

  2. takeWhile 与 filter 的区别

  3. 三级排序

  4. 包含 null 值的排序

  5. 二维流扁平映射

  6. 三维流扁平映射

  7. 用 stream 打印九九乘法表

  8. 用 stream 生成斐波那契数列的前 10 项

    Stream.iterate(new int[]{1, 1}, x -> new int[]{x[1], x[0] + x[1]})
        .map(x -> x[0])
        .limit(10)
    
  9. 自定义 Collector 求平均

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1716690.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Qt秘籍】[005]-Qt的首次邂逅-创建

一、如何创建文件&#xff1f; 当我们打开Qt Creator&#xff0c;你会发现整个界面类目繁多。现在&#xff0c;让我们直接开始新建一个项目。 1.点击左上角的“文件”>点击“新建文件或项目” 2.如图&#xff0c;选择“Application”>“Qt Wifgets application”> “…

发现没:随便搞个B端页面,就想在客户那里过关,难啦。

客户对B端界面要求越来越高的原因可以有以下几点&#xff1a; 用户体验要求提升&#xff1a;随着用户对移动应用和网页的使用经验增加&#xff0c;他们对于界面的交互、流畅性和易用性要求也越来越高。他们希望能够在使用B端应用时&#xff0c;能够快速、方便地完成任务&#…

Android下HWC以及drm_hwcomposer普法(上)

Android下HWC以及drm_hwcomposer普法(上) 引言 按摩得全套&#xff0c;错了&#xff0c;做事情得全套&#xff0c;普法分析也是如此。drm_hwcomposer如果对Android图形栈有一定研究的童鞋们应该知道它是Android提供的一个的图形后端合成处理HAL模块的实现。但是在分析这个之前…

教程来咯!如何在Windows10中设置代理IP?

很多用户在使用win10系统的时候&#xff0c;网络设置都是默认的&#xff0c;一般情况下代理服务器都是关闭的状态&#xff0c;而在一些特殊情况下&#xff0c;需要设置代理地址启动功能使用&#xff0c;有不少的用户不知道应该怎么进行设置添加&#xff0c;接下来就和各位用户们…

Docker容器技术----什么是容器?

目录 什么是容器&#xff1f; 容器与虚拟机的区别 为什么要使用容器&#xff1f; 为什么这几年来容器技术一下子流行了起来&#xff1f; 什么是容器&#xff1f; 先看看我们生活中的容器。生活中见到的瓶瓶罐罐&#xff0c;就是容器&#xff0c;用来成放东西&#xff0c;并…

Honor of Kings 2024.03.29 Ban for 3 day

我又被举报消极然后禁赛 都说了别选蔡文姬&#xff0c;对面三个肉&#xff0c;非要选个软辅助 吐槽下这游戏策划&#xff1a;游戏体验感越来越差&#xff0c;公正也很差 对说了对面4个法师&#xff0c;就是不出魔抗&#xff0c;把把都是0-N开局&#xff0c;到底谁消极啊&#x…

NI PXIe-7857R与PXIe-8842的区别

一、NI PXIe-7857R 类型&#xff1a; FPGA模块&#xff1a;基于FPGA的可编程I/O模块。 主要功能&#xff1a; FPGA处理&#xff1a;包含Xilinx Kintex-7 FPGA&#xff0c;支持自定义逻辑和处理。 I/O接口&#xff1a;提供丰富的模拟和数字I/O通道。 高速数据处理&#xff1a…

IO系列(十) -TCP 滑动窗口原理解析

一、摘要 之前在知乎上分享网络编程知识文章的时候&#xff0c;有个网友私信给我留言了一条“能不能写一篇关于 TCP 滑动窗口原理的文章”。 当时没有立即回复&#xff0c;经过查询多方资料&#xff0c;发现这个 TCP 真的非常非常的复杂&#xff0c;就像一个清澈的小沟&#…

【Linux 网络编程】协议的分层知识!

文章目录 1. 计算机网络背景2. 认识 "协议"3. 协议分层 1. 计算机网络背景 网络互联: 多台计算机连接在一起, 完成数据共享; &#x1f34e;局域网&#xff08;LAN----Local Area Network&#xff09;: 计算机数量更多了, 通过交换机和路由器连接。 &#x1f34e; 广…

重学java 51.Collections集合工具类、泛型

"我已不在地坛&#xff0c;地坛在我" —— 《想念地坛》 24.5.28 一、Collections集合工具类 1.概述:集合工具类 2.特点: a.构造私有 b.方法都是静态的 3.使用:类名直接调用 4.方法: static <T> boolean addAll(collection<? super T>c,T... el…

【Node】nodejs版本管理切换工具

1、nvm介绍 Node Version Manager&#xff08;NVM&#xff09; 是一种用于管理多个主动节点.js版本的工具。 Node.js平台&#xff0c;Node.js工具社区和Node.js库是快速移动的目标 - 在一个Node.js版本下可能有效的方法不能保证适用于另一个版本的Node.js。因此&#xff0c;用户…

人工智能的数学基础(高数)

&#x1f31e;欢迎来到人工智能的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年5月29日&…

性能测试(基于Jmeter)

性能指标 RT&#xff08;Response Time&#xff09;响应时间&#xff1a;指的是用户从客户端发起请求开始到服务端返回结束&#xff0c;整个过程所耗费的时间 HPS&#xff08;Hits Per Second&#xff09;&#xff1a; 每秒点击次数&#xff0c;单位&#xff1a;次/秒 TPS&am…

【FISCO BCOS 3.0】一、新版本搭链介绍

目录 一、区块链种类的变化 二、搭链演示 1.单群组区块链&#xff08;Air版本&#xff09; 2.多群组区块链&#xff08;Pro版本&#xff09; 3.可扩展区块链&#xff08;Max版本&#xff09; FISCO BCOS的发展速度如日中天&#xff0c;对于稳定的2.0版本而言&#xff0c;偶…

看完这篇抖音小店选品秘籍!难道还愁不会选品吗?

大家好&#xff0c;我是喷火龙。 做抖音小店&#xff0c;没有什么花里胡哨的东西&#xff0c;核心就是我们的产品&#xff0c;把大部分精力用到选品上肯定是没错的。 选品之前一定要做好店铺的定位&#xff0c;确定好自己的主营类目&#xff0c;主营类目至少要占店铺产品的70…

Windows系统部署YOLOv5 v6.1版本的训练与推理环境保姆级教程

文章目录 一 概述二 依赖环境(prerequisites)2.1 硬件环境2.2 软件环境 三 环境安装3.1 创建并激活虚拟环境3.2 安装Pytorch与torchvision3.3 校验Pytorch安装3.4 下载 YOLOv5 v6.1 源码3.5 安装 YOLOv5 依赖3.6 下载预训练模型3.7 安装其他依赖3.8 测试环境安装3.9 测试训练流…

el-image本地图片不显示,提示加载失败

问题描述&#xff1a;el-image使用本地图片不显示&#xff0c;提示加载失败。 <el-image src"../../assets/img/value.png"></el-image> 解决方法&#xff1a;src用里面加个require&#xff0c;注意给 src 属性加: <el-image :src"require(../..…

centos7离线安装pthon3.8

centos7离线安装pthon3.8 因服务器无外网环境&#xff0c;所以事先需要把所有离线的依赖都准备好。 安装前的准备 先在有外网环境的机器上准备依赖 安装 centos-release-scl 第三方yum源 yum install centos-release-scl安装 yum 依赖下载插件 yum install yum-plugin-do…

《C语言深度解剖》(16):C语言的文件读写操作

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《C语言深度解剖》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多C语言深度解剖点击专栏链接查看&…

STL-priority_queue的使用及其模拟实现

优先级队列(priority_queue)默认使用vector作为其底层存储数据的容器&#xff0c;在vector上又使用了堆算法将vector中的元素构造成堆的结构&#xff0c;因此priority_queue就是堆&#xff0c;所有需要用到堆的位置&#xff0c;都可以考虑使用priority_queue。 注意&#xff1…