Java 之 lambda 表达式(二)---- Stream 操作 API

news2024/9/28 5:16:09

目录

一. 前言

二. Stream 创建

2.1. 使用集合来创建 Stream

2.2. 使用数组创建 Stream

2.3. 由值创建 Stream

2.4. 由函数创建无限流 Stream

2.5. 代码示例

三. Stream 操作

3.1. 中间型操作

3.1.1. filter()

3.1.2. map()

3.1.3. mapToInt()、mapToLong()、mapToDouble()

3.1.4. flatMap()

3.1.5. peek()

3.1.6. skip()

3.1.7. limit()

3.1.8. concat()

3.1.9. distinct()

3.1.10. sorted()

3.2. 终结型操作

3.2.1. count()

3.2.2. max()、min()

3.2.3. findFirst()、findAny()

3.2.4. anyMatch()、noneMatch() 和 allMatch()

3.2.5. reduce()

3.2.6. collect()

3.3. Collector 收集

3.3.1. toList()、toSet()、toCollection()

3.3.2. toMap()、toConcurrentMap()

3.3.3. mapping()、collectingAndThen()

3.3.4. groupingBy()

3.3.5. partitioningBy()

3.3.6. joining()

3.3.7. 统计相关 counting()、maxBy()、minBy()、averagingDouble()、summingInt()、summarizingInt()

3.3.8. reducing()


一. 前言

    Stream,就是 JDK8 依托于函数式编程特性为集合类库做的一个类库,它其实就是 JDK 提供的函数式接口的最佳实践。它能让我们通过 lambda 表达式更简明扼要的以流水线的方式去处理集合内的数据,可以很轻松的完成诸如:过滤、分组、收集、归约这类操作。关于 lambda 的整体介绍请参见《Java 之 lambda 表达式(一)》。

Stream 特点:
1. Stream 自己不会存储元素;
2. Stream 不会改变源对象,Stream 操作会返回一个新的Stream;
3. Stream 操作是延迟执行的,这意味着等到获取结果时,Stream 才会执行。

Stream 操作的三个步骤:
1. Stream 创建,由一个数据源如集合、数组来获取一个Stream;
2. 中间操作:一个中间操作链,对数据源的数据进行处理;
3. 终止操作:执行中间操作链并产生结果。

Stream 的操作步骤示例图:

二. Stream 创建

2.1. 使用集合来创建 Stream

Java8 中的 Collection 接口被扩展,提供了两个获取Stream的方法:

// 返回一个顺序流
default Stream<R> stream();

// 返回一个并行流
default Stream<E> parallelStream();

2.2. 使用数组创建 Stream

Java8 中的 Arrays 的静态方法 stream() 可以获取流:

static <T> Stream<T> stream(T[] array);

static IntStream<T> stream(int[] array);

static LongStream<T> stream(long[] array);

static DoubleStream<T> stream(double[] array);

2.3. 由值创建 Stream

使用 Stream 的静态方法 of 来创建一个流,该方法可以接收任意数量的参数:

static<T> Stream<T> of(T... values)

2.4. 由函数创建无限流 Stream

可以使用 Stream 的静态方法 iterate() 和 generate() 创建无限流:

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

public static<T> Stream<T> generate(Supplier<T> s)

2.5. 代码示例

public void test01() {
    // 1.通过Collection系列集合提供的stream()或parallelStream()获取
    ArrayList<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();

    // 2.通过Arrays中的静态方法stream()获取
    String[] strings=new String[10];
    Stream<String> stream2 = Arrays.stream(strings);

    // 3.通过Stream类中的静态方法of()获取
    Stream<String> stream3 = Stream.of(strings);

    // 4.创建无限流
    // 4.1迭代
    Stream<Integer> stream4 = Stream.iterate(0, x-> x + 2);

    // 4.2生成
    Stream<Double> stream5 = Stream.generate(() -> Math.random());
}

三. Stream 操作

    Stream 的操作大致分为两类:中间型操作终结型操作。其中转换型操作又分为有状态和无状态两类。有状态是本次的结果需要依赖于前面的处理结果,而无状态则是不依赖。简单来讲就是无状态方法可以互相调换位置,而有状态方法不能调换位置。

    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而是在终止操作时一次性全部处理,称为“惰性求值”。

    惰性求值即 Java8 的 Stream 操作。惰性求值操作的结果也是 Stream,惰性求值可以像建造者模式一样链式使用,最后再使用及早求值得到最终结果。

3.1. 中间型操作

中间型操作就是返回值依旧是 Stream 类型的方法。API 如下:

API功能说明无状态操作
filter()按照条件过滤符合要求的元素, 返回新的stream流。
map()将已有元素转换为另一个对象类型,一对一逻辑,返回新的stream流
mapToInt()返回一个IntStream,将一个流中的元素转换为 int 类型。
mapToLong()返回一个LongStream,将一个流中的元素转换为 long 类型。
mapToDouble()返回一个DoubleStream,将一个流中的元素转换为 double 类型。
flatMap()将已有元素转换为另一个对象类型,一对多逻辑,即原来一个元素对象可能会转换为1个或者多个新类型的元素,返回新的stream流

flatMapToInt()

返回一个IntStream,该结果包含将流中的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容而得到的结果。
flatMapToLong()返回一个LongStream,该结果包含将流中的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容而得到的结果。
flatMapToDouble()返回一个DoubleStream,该结果包含将流中的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容而得到的结果。
peek()对stream流中的每个元素进行逐个遍历处理,返回处理后的stream流
limit()仅保留集合前面指定个数的元素,返回新的stream流
skip()跳过集合前面指定个数的元素,返回新的stream流
concat()将两个流的数据合并起来为1个新的流,返回新的stream流
distinct()对Stream中所有元素进行去重,返回新的stream流
sorted()对stream中所有的元素按照指定规则进行排序,返回新的stream流
takeWhile()JDK9新增,传入一个断言参数当第一次断言为false时停止,返回前面断言为true的元素。
dropWhile()JDK9新增,传入一个断言参数当第一次断言为false时停止,删除前面断言为true的元素。

3.1.1. filter()

Stream<T> filter(Predicate<? super T> predicate); 起过滤筛选的作用,内部就是Predicate接口。

public class TestCase {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>(3);
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));
        List<Student> list = studentList.stream()
            .filter(stu -> stu.getStature() < 180)
            .collect(Collectors.toList());
        System.out.println(list);
    }
}
// 输出结果:

[Student{name='流华追梦1', age=22, stature=175}]

3.1.2. map()

<R> Stream<R> map(Function<? super T, ? extends R> mapper); 映射函数,实现转换功能,内部就是Function接口。

public class MapDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>(3);
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));
        List<String> list = studentList.stream().map(student -> student.getName())
                .collect(Collectors.toList());
        // 方法引用写法
        // List<String> list = studentList.stream().map(Student::getName)
        //        .collect(Collectors.toList());
        System.out.println(list);
    }
}
// 输出结果:

[流华追梦1, 流华追梦2, 流华追梦3]

3.1.3. mapToInt()、mapToLong()、mapToDouble()

mapToInt()、mapToLong()、mapToDouble() 方法的功能和 map() 方法一样,只不过返回的结果已经没有泛型,已经明确是 int、long、double 类型的流了。

public class MapToDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
        }};
        // mapToInt
        List<Integer> intList = list.stream()
                .mapToInt(s -> Integer.valueOf(s))
                // 一定要有 mapToObj,因为 mapToInt 返回的是 IntStream,因为已经确定是 int 类型了,所以没有泛型的,
                // 而 Collectors.toList() 强制要求有泛型的流,所以需要使用 mapToObj方法返回有泛型的流
                .mapToObj(s -> s)
                .collect(Collectors.toList());
        System.out.println(intList);

        // mapToLong
        List<Long> longList = list.stream()
                .mapToLong(s -> Long.valueOf(s))
                .mapToObj(s -> s)
                .collect(Collectors.toList());
        System.out.println(longList);

        // mapToDouble
        List<Double> doubleList = list.stream()
                .mapToDouble(s -> Double.valueOf(s))
                .mapToObj(s -> s)
                .collect(Collectors.toList());
        System.out.println(doubleList);
    }
}
// 运行结果:
[1, 2, 3]
[1, 2, 3]
[1.0, 2.0, 3.0]

3.1.4. flatMap()

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper); 将多个Stream 合并为一个 Stream,用于将原有二维结构扁平化。

public class FlatMapDemo {
    public static void main(String[] args) {
        List<Student> studentList1 = new ArrayList<>();
        studentList1.add(new Student("流华追梦1", 22, 175));
        studentList1.add(new Student("流华追梦2", 40, 180));
        studentList1.add(new Student("流华追梦3", 50, 185));

        List<Student> studentList2 = new ArrayList<>();
        studentList2.add(new Student("流华追梦4", 25, 183));
        studentList2.add(new Student("流华追梦5", 48, 176));

        List<List<Student>> studentList = new ArrayList<>();
        studentList.add(studentList1);
        studentList.add(studentList2);
        List<Student> list = studentList.stream().flatMap(subList -> subList.stream()).collect(Collectors.toList());
        System.out.println(list);
    }
}
// 运行结果:
[Student{name='流华追梦1', age=22, stature=175}
, Student{name='流华追梦2', age=40, stature=180}
, Student{name='流华追梦3', age=50, stature=185}
, Student{name='流华追梦4', age=25, stature=183}
, Student{name='流华追梦5', age=48, stature=176}
]

3.1.5. peek()

Stream<T> peek(Consumer<? super T> action); 对 Stream 流中的每个元素进行逐个遍历处理,返回 Stream 流。

public class PeekDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));

        List<Student> list = studentList.stream()
                .peek(student -> System.out.println(student.getName()))
                .collect(Collectors.toList());
        System.out.println(list);
    }
}
// 运行结果:
流华追梦1
流华追梦2
流华追梦3
[Student{name='流华追梦1', age=22, stature=175}
, Student{name='流华追梦2', age=40, stature=180}
, Student{name='流华追梦3', age=50, stature=185}
]

3.1.6. skip()

Stream<T> skip(long n); 跳过集合前面指定个数的元素,返回新的 Stream 流,若流中的元素不足,则返回空流。与 limit 互补。

public class SkipDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));

        List<Student> list = studentList.stream()
                .skip(1)
                .collect(Collectors.toList());
        System.out.println(list);
    }
}
// 运行结果:
流华追梦2
流华追梦3
[Student{name='流华追梦2', age=40, stature=180}
, Student{name='流华追梦3', age=50, stature=185}
]

3.1.7. limit()

Stream<T> limit(long maxSize); 仅保留集合前面指定个数的元素,返回新的stream流,使其元素不超过给定数量。

public class LimitDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));

        List<Student> list = studentList.stream()
                .filter(student -> student.getStature() > 175)
                .limit(1)
                .collect(Collectors.toList());
        System.out.println(list);
    }
}
// 运行结果:
流华追梦2
[Student{name='流华追梦2', age=40, stature=180}
]

3.1.8. concat()

<T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b); 将两个流的数据合并起来为1个新的流,返回新的 Stream 流。

public class ConcatDemo {
    public static void main(String[] args) {
        List<Student> studentList1 = new ArrayList<>();
        studentList1.add(new Student("流华追梦1", 22, 175));
        studentList1.add(new Student("流华追梦2", 40, 180));
        studentList1.add(new Student("流华追梦3", 50, 185));

        List<Student> studentList2 = new ArrayList<>();
        studentList2.add(new Student("流华追梦4", 25, 183));
        studentList2.add(new Student("流华追梦5", 48, 176));

        List<Student> list = Stream.concat(studentList1.stream(), studentList2.stream()).collect(Collectors.toList());
        System.out.println(list);
    }
}
// 运行结果:
[Student{name='流华追梦1', age=22, stature=175}
, Student{name='流华追梦2', age=40, stature=180}
, Student{name='流华追梦3', age=50, stature=185}
, Student{name='流华追梦4', age=25, stature=183}
, Student{name='流华追梦5', age=48, stature=176}
]

3.1.9. distinct()

Stream<T> distinct(); 对 Stream 中所有元素进行去重,返回新的 Stream流。

示例1:

public static void DistinctDemo() {
	List<String> list = new ArrayList<String>() {{
		add("1");
		add("2");
		add("2");
		add("5");
		add("3");
	}};
	List<String> strList = list.stream()
			.distinct()
			.collect(Collectors.toList());
	System.out.println(strList);
}
运行结果:
[1, 2, 5, 3]

示例2:

public static void DistinctDemo() {
	List<Student> studentList = new ArrayList<>();
    studentList.add(new Student("流华追梦1", 22, 175));
    studentList.add(new Student("流华追梦2", 40, 180));
    studentList.add(new Student("流华追梦3", 50, 185));
    studentList.add(new Student("流华追梦1", 22, 183));
    studentList.add(new Student("流华追梦2", 48, 176));

    // 根据 name 进行去重
    List<Student> distinctUsers1 = studentList.stream()
            .filter(distinctByKey(Student::getName))
            .collect(Collectors.toList());
    System.out.println(distinctUsers1);

    // 根据 name + age 进行去重
    List<Student> distinctUsers2 = studentList.stream()
            .filter(distinctByKey(item -> item.getName() + item.getAge()))
            .collect(Collectors.toList());
    System.out.println(distinctUsers2);
}
// 运行结果:
[{"流华追梦1", 22, 175}, {"流华追梦2", 40, 180}, {"流华追梦3", 50, 185}]
[{"流华追梦1", 22, 175}, {"流华追梦2", 40, 180}, {"流华追梦3", 50, 185}, {"流华追梦2", 48, 176}]

3.1.10. sorted()

sorted() 方法提供了排序的功能,并且允许我们自定义排序。

public static void SortedDemo() {
	List<Student> studentList = new ArrayList<>();
    studentList.add(new Student("流华追梦1", 22, 175));
    studentList.add(new Student("流华追梦2", 40, 180));
    studentList.add(new Student("流华追梦3", 50, 185));
    studentList.add(new Student("流华追梦4", 22, 183));
    studentList.add(new Student("流华追梦5", 48, 176));

    studentList.sort((v1, v2)-> v1.getAge() - v2.getAge());
    // studentList.sort(Comparator.comparingInt(Student::getAge));
    // studentList.sort(new Comparator<Student>() {
    //        @Override
    //        public int compare(Student o1, Student o2) {
    //            return o1.getAge() - o2.getAge();
    //        }
    //    });
    System.out.println(studentList);
}
// 运行结果:
[{"流华追梦1", 22, 175}, {"流华追梦4", 25, 183}, {"流华追梦2", 48, 176}, {"流华追梦5", 48, 176}, {"流华追梦3", 50, 185}]

3.2. 终结型操作

终结型操作与中间型相反,返回值是非 Stream 类型的。API 如下:

API功能说明
count()返回stream处理后最终的元素个数
max()返回stream处理后的元素最大值
min()返回stream处理后的元素最小值
findFirst()找到第一个符合条件的元素时则终止流处理
findAny()找到任何一个符合条件的元素时则退出流处理,这个对于串行流时与findFirst相同,对于并行流时比较高效,任何分片中找到都会终止后续计算逻辑
anyMatch()返回一个boolean值,类似于isContains(),用于判断是否有符合条件的元素
allMatch()返回一个boolean值,用于判断是否所有元素都符合条件
noneMatch()返回一个boolean值, 用于判断是否所有元素都不符合条件
collect()将流转换为指定的类型,通过Collectors进行指定
reduce()将一个Stream中的所有元素反复结合起来,得到一个结果
toArray()将流转换为数组
iterator()将流转换为Iterator对象
foreach()无返回值,对元素进行逐个遍历,然后执行给定的处理逻辑

3.2.1. count()

long count(); 统计功能,一般都是结合 filter() 使用,先筛选出我们需要的再统计即可。

public class CountDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>(3);
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));
        long count = studentList.stream().filter(s1 -> s1.getAge() < 45).count();
        System.out.println("年龄小于45岁的人数是:" + count);
    }
}
// 输出结果:
年龄小于45岁的人数是:2

3.2.2. max()、min()

Optional<T> max(Comparator<? super T> comparator);Optional<T> min(Comparator<? super T> comparator); 在集合中求最大值和最小值。

public class MaxMinDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>(3);
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));
        Optional<Student> max = studentList.stream()
                .max(Comparator.comparing(Student::getAge));
        Optional<Student> min = studentList.stream()
                .min(Comparator.comparing(Student::getAge));
        // 判断是否有值
        max.ifPresent(System.out::println);
        min.ifPresent(System.out::println);
    }
}
// 输出结果:
Student{name='流华追梦3', age=50, stature=185}
Student{name='流华追梦1', age=22, stature=175}

3.2.3. findFirst()、findAny()

Optional<T> findFirst(); 和 Optional<T> findAny(); 返回流中的第一个元素和任意元素。

public class FindDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>(3);
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));
        Optional<Student> student = studentList.stream().filter(s1 -> s1.getAge() < 60).findFirst();
        System.out.println("年龄小于60岁的第1个人是:" + stduent.get().getName());

        Optional<Student> student = studentList.stream().filter(s1 -> s1.getAge() < 60).findAny();
        System.out.println("年龄小于60岁的任1个人是:" + stduent.get().getName());
    }
}
// 输出结果:
年龄小于60岁的第1个人是:流华追梦1
年龄小于60岁的任1个人是:流华追梦1

3.2.4. anyMatch()、noneMatch() 和 allMatch()

boolean anyMatch(Predicate<? super T> predicate); 检查是否至少匹配一个元素。
boolean allMatch(Predicate<? super T> predicate); 检查是否匹配所有元素。
boolean noneMatch(Predicate<? super T> predicate); 检查是否没有匹配所有元素。

public class MatchDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>(3);
        studentList.add(new Student("流华追梦1", 22, 175));
        studentList.add(new Student("流华追梦2", 40, 180));
        studentList.add(new Student("流华追梦3", 50, 185));
        boolean anyMatched = studentList.stream().anyMatch(s1 -> s1.getAge() > 40);
        System.out.println("有年龄大于40岁的人吗:" + anyMatched);

        boolean allMatched = studentList.stream().allMatch(s1 -> s1.getAge() > 40);
        System.out.println("年龄全部大于40岁吗:" + allMatched);

        boolean noneMatched = studentList.stream().noneMatch(s1 -> s1.getAge() > 40);
        System.out.println("年龄全部没有大于40岁的人吗:" + noneMatched);
    }
}
// 输出结果:
有年龄大于40岁的人吗:true
年龄全部大于40岁吗:false
年龄全部没有大于40岁的人吗:false

3.2.5. reduce()

T reduce(T identity, BinaryOperator<T> accumulator); 可以实现从一组值中生成一个值。

public class ReduceSumDemo {
    public static void main(String[] args) {
        Integer reduce = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
        System.out.println(reduce);
    }
}
// 输出结果:
15

3.2.6. collect()

<R, A> R collect(Collector<? super T, A, R> collector); 将流转换为 List,Set 对应 toSet(),Map对应 toMap() 等。

public class TestCase {
    public static void main(String[] args) {
        List<Student> studentList = Stream.of(
                new Student("流华追梦1", 22, 175),
                new Student("流华追梦2", 40, 180),
                new Student("流华追梦3", 50, 185)).collect(Collectors.toList());
        System.out.println(studentList);
    }
}
输出结果:
[Student{name='流华追梦1', age=22, stature=175},
Student{name='流华追梦2', age=40, stature=180},
Student{name='流华追梦3', age=50, stature=185}]

3.3. Collector 收集

    Collector 接口中的方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。Collectors 实用类提供了很多静态方法,可以方便地创建常用的收集器实例,具体方法如下表:

方法返回类型作用
toListList<T>把流中元素收集到 List
toSetSet<T>把流中元素收集到 Set
toCollectionCollection<T>把流中元素收集到创建的集合
toMap

Map<K,U>

把流中元素收集到 Map
toConcurrentMap

ConcurrentMap<K,U>

把流中元素收集到 ConcurrentMap
mapping

Collector<T, ?, R>

首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳。 类似与Stream的map方法。
countingLong计算流中元素的个数
summingIntInteger对流中元素的整数属性求和
summingLongLong对流中元素的Long属性求和
summingDoubleDouble对流中元素的Double属性求和
averagingIntDouble计算流中元素Integer属性的平均值
averagingLongLong计算流中元素Long属性的平均值
averagingDoubleDouble计算流中元素Double属性的平均值

summarizingInt

Collector<T, ?, IntSummaryStatistics>

收集流中Integer属性的统计值。如平均值
summarizingLongCollector<T, ?, LongSummaryStatistics>收集流中Long属性的统计值。如平均值
summarizingDoubleCollector<T, ?, DoubleSummaryStatistics>收集流中Double属性的统计值。如平均值
joining

Collector<CharSequence, ?, String>

连接流中每个字符串
maxBy

Collector<T, ?, Optional<T>>

根据比较器选择最大值
minBy

Collector<T, ?, Optional<T>>

根据比较器选择最小值
reducing归约产生的类型从一个作为累加器的初始值开始,利用 BinaryOperator 与流中元素逐个结合,从而归约成单个值
collectingAndThen置换函数返回的类型包裹另一个收集器,对其结果置换的函数
groupingBy

Map<K, List<T>>

根据某属性值对流分组,属性为K,结果为V
partitioningBy

Map<Boolean, List<T>>

根据 true 或 false 进行分区

3.3.1. toList()、toSet()、toCollection()

将流转换为其他形式,收集到新的集合上,包括Collectors.toList()、Collectors.toSet()、Collectors.toCollection(集合对象)。

@Test
public void test01() {
	List<Student> studentList = new ArrayList<>();
	studentList.add(new Student("流华追梦1", 22, 175));
	studentList.add(new Student("流华追梦2", 40, 180));
	studentList.add(new Student("流华追梦3", 50, 185));
	studentList.add(new Student("流华追梦4", 22, 183));
	studentList.add(new Student("流华追梦5", 48, 176));

	List<String> list = studentList.stream().map(Student::getName).collect(Collectors.toList());
	list.forEach(System.out::println);
	System.out.println("-----------------");
	Set<String> set = studentList.stream().map(Student::getName).collect(Collectors.toSet());
	set.forEach(System.out::println);
	System.out.println("-----------------");
	HashSet<String> hashSet = studentList.stream().map(Student::getName).collect(Collectors.toCollection(HashSet::new));
	hashSet.forEach(System.out::println);
}
// 运行结果:
流华追梦1
流华追梦2
流华追梦3
流华追梦4
流华追梦5
-----------------
流华追梦3
流华追梦2
流华追梦5
流华追梦4
流华追梦1
-----------------
流华追梦3
流华追梦2
流华追梦5
流华追梦4
流华追梦1

3.3.2. toMap()、toConcurrentMap()

    toMap() 方法是根据给定的键生成器和值生成器生成的键和值保存到一个 map 中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的 map。

    toConcurrentMap() 的使用与 toMap() 基本一致, 只不过 toConcurrentMap() 用于处理并发请求,它生成的 map 是 ConcurrentHashMap。

toMap() 有三个重载的方法:

public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
								Function<? super T, ? extends U> valueMapper) {
	return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}

public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
								Function<? super T, ? extends U> valueMapper,
								BinaryOperator<U> mergeFunction) {
	return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
							Function<? super T, ? extends U> valueMapper,
							BinaryOperator<U> mergeFunction,
							Supplier<M> mapSupplier) {
	BiConsumer<M, T> accumulator
			= (map, element) -> map.merge(keyMapper.apply(element),
										  valueMapper.apply(element), mergeFunction);
	return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}

三个重载的方法,最终都是调用第三个方法来实现,第一个方法中默认指定了 key 重复的处理方式和 map 的生成方式;而第二个方法默认指定了 map 的生成方式,用户可以自定义 key 重复的处理方式。

Map<Integer, Student> map1 = stream.collect(Collectors.toMap(Student::getId, v->v));

Map<Integer, String> map2 = stream.collect(Collectors.toMap(Student::getId, Student::getName, (a, b)->a));

Map<Integer, String> map3 = stream.collect(Collectors.toMap(Student::getId, Student::getName, (a, b)->a, HashMap::new));

3.3.3. mapping()、collectingAndThen()

mapping() 对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳。 类似与Stream的map方法。

collectingAndThen:在归纳动作结束之后,对归纳的结果进行再处理。

public void mappingDemo() {
    List<Student> studentList = new ArrayList<>();
	studentList.add(new Student("流华追梦1", 22, 175));
	studentList.add(new Student("流华追梦2", 40, 180));
	studentList.add(new Student("流华追梦3", 50, 185));
	studentList.add(new Student("流华追梦4", 22, 183));
	studentList.add(new Student("流华追梦5", 48, 176));

    String nameByAge = studentList.stream().collect(Collectors.mapping(Student::getName, Collectors.joining(",", "[", "]")));
    System.out.println(nameByAge);
	
    nameByAge = studentList.stream().map(student -> student.getName()).collect(Collectors.joining(",", "[", "]"));
    System.out.println(nameByAge);

    Integer size = studentList.collect(Collectors.collectingAndThen(Collectors.mapping(Student::getAge, Collectors.toList()), o -> o.size()));
}
运行结果:
[流华追梦1,流华追梦2,流华追梦3,流华追梦4,流华追梦5]
[流华追梦1,流华追梦2,流华追梦3,流华追梦4,流华追梦5]

3.3.4. groupingBy()

根据某属性值对流分组,属性为K,结果为V。

public void groupDemo() {
    List<Student> studentList = new ArrayList<>();
	studentList.add(new Student("流华追梦1", 22, 175));
	studentList.add(new Student("流华追梦2", 40, 180));
	studentList.add(new Student("流华追梦3", 50, 185));
	studentList.add(new Student("流华追梦4", 22, 183));
	studentList.add(new Student("流华追梦5", 48, 176));

	Map<Integer, String> nameByAgeMap = studentList.stream().collect(
			Collectors.groupingBy(Student::getAge, Collectors.mapping(Student::getName, Collectors.joining(",", "[", "]"))));
	nameByAgeMap.forEach((k, v) -> System.out.println("Age:" + k + "  Students: " + v));
}
// 运行结果:
Age:48  Students: [流华追梦5]
Age:50  Students: [流华追梦3]
Age:22  Students: [流华追梦1,流华追梦4]
Age:40  Students: [流华追梦2]

3.3.5. partitioningBy()

该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的集合。

public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
	return partitioningBy(predicate, toList());
}

public static <T, D, A>
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
												Collector<? super T, A, D> downstream) {
	......
}

从上面的重载方法中可以看出,partitioningBy 与 groupingBy 类似, 只不过partitioningBy 生成的map的key的类型限制只能是Boolean类型。

public void partitionDemo() {
    List<Student> studentList = new ArrayList<>();
	studentList.add(new Student("流华追梦1", 22, 175));
	studentList.add(new Student("流华追梦2", 40, 180));
	studentList.add(new Student("流华追梦3", 50, 185));
	studentList.add(new Student("流华追梦4", 22, 183));
	studentList.add(new Student("流华追梦5", 48, 176));

	Map<Boolean, List<Student>> m4 = stream.collect(Collectors.partitioningBy(stu -> stu.getAge() > 40));
 
	Map<Boolean, Set<Student>> m5 = stream.collect(Collectors.partitioningBy(stu -> stu.getAge() > 60, Collectors.toSet()));
}

3.3.6. joining()

连接流中每个字符串。

Stream<String> stream = Stream.of("1", "2", "3", "4", "5", "6");
String s = stream.collect(Collectors.joining(",", "prefix", "suffix"));

// 运行结果:
prefix1,2,3,4,5,6suffix

3.3.7. 统计相关 counting()、maxBy()、minBy()、averagingDouble()、summingInt()、summarizingInt()

数组统计相关方法,包括 counting()、maxBy()、minBy()、averagingDouble()、summingInt()、summarizingInt() 等方法。

public void test01() {
    List<Student> studentList = new ArrayList<>();
	studentList.add(new Student("流华追梦1", 22, 175));
	studentList.add(new Student("流华追梦2", 40, 180));
	studentList.add(new Student("流华追梦3", 50, 185));
	studentList.add(new Student("流华追梦4", 22, 183));
	studentList.add(new Student("流华追梦5", 48, 176));

	// 总数
	Long sum= studentList.stream().collect(Collectors.counting());
	System.out.println(sum);
	// 平均值
	Double averageAge = studentList.stream().collect(Collectors.averagingDouble(Student::getAge));
	System.out.println(averageAge);
	// 某属性的综合
	Integer ageSum = studentList.stream().collect(Collectors.summingInt(Student::getAge));
	System.out.println(ageSum);
	// 最大值
	Optional<Student> maxAgeEmployee = studentList.stream().collect(Collectors.maxBy((e1, e2) -> e1.getAge().compareTo(e2.getAge())));
	System.out.println(maxAgeEmployee.get());
	// 最小值
	Optional<Student> minAgeEmployee = studentList.stream().collect(Collectors.minBy((e1, e2) -> e1.getAge().compareTo(e2.getAge())));
	System.out.println(minAgeEmployee.get());
	// 所有数据
	IntSummaryStatistics totalData = studentList.stream().collect(Collectors.summarizingInt(Student::getAge));
	System.out.println(totalData);
}
// 运行结果:
5
36.4
182
Student{name='流华追梦3', age=50, stature=185}
Student{name='流华追梦1', age=22, stature=175}
IntSummaryStatistics{count=5, sum=182, min=22, average=36.400000, max=50}

3.3.8. reducing()

reducing() 方法有三个重载方法,其实是和 Stream 里的三个 reduce() 方法对应的,二者是可以替换使用的,作用完全一致,也是对流中的元素做统计归纳作用。

public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) {
    ......
}
 
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {
    ......
}
 
public static <T, U> Collector<T, ?, U> reducing(U identity,Function mapper, BinaryOperator<U> op) {
    ......
}
List<String> list2 = Arrays.asList("123","456","789","qaz","wsx","edc");
 
Optional<Integer> optional = list2.stream().map(String::length).collect(Collectors.reducing(Integer::sum));
System.out.println("reducing1: " + optional.get());

Integer sum1 = list2.stream().map(String::length).collect(Collectors.reducing(0, Integer::sum));
System.out.println("reducing2: " + sum1);
 
Integer sum2 = list2.stream().limit(4).collect(Collectors.reducing(0, String::length, Integer::sum));
System.out.println("reducing2: " + sum2);
// 运行结果:
reducing1: 18
reducing2: 18
reducing3: 18

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

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

相关文章

ESP32-Web-Server编程- JS 基础5

ESP32-Web-Server编程- JS 基础5 概述 JS 编程内容颇多&#xff0c;我们提供一些简单的示例&#xff0c;先玩再学&#xff0c;边玩边学。 示例1-演示通过 JS 进行温度转换 资源链接 对应示例的 code 链接 &#xff08;点击直达代码仓库&#xff09; 示例2-增加网页弹窗 演…

【数据结构—单链表的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 链表的概念及结构 2. 单链表的实现 2.1单链表头文件——功能函数的定义 2.2单链表源文件——功能函数的实现 2.3 单链表源文件——功能的测试 3.具体的理解操作…

Selenium 学习(0.14)——软件测试之测试用例设计方法——因果图法2【基本步骤及案例】

1、因果图法的基本步骤 2、案例分析 1&#xff09;分析原因和结果 2&#xff09;关联原因和结果 投入1元5角或2元&#xff0c;按下“可乐”&#xff0c;送出“可乐”【暂时忽略找零】 投入2元&#xff0c;按下“可乐”或“雪碧”。找零5角&#xff0c;送出“可乐”或“雪…

vue中keep-alive的使用

什么是keep-alive&#xff1f; keep-alive是一个内置组件&#xff0c;用于缓存和管理组件的状态。 当 keep-alive包裹一个组件时&#xff0c;这个组件的状态将会被缓存起来&#xff0c;而不是每次重新渲染。这在多个视图之间切换时特别有用&#xff0c;可以避免重复的创建和销…

uniapp前端+python后端=微信小程序支付到底怎么开发???国内的资料为什么没一篇能讲清楚,简简单单的只需要3步就可以了-V2版本

一.微信小程序支付 真的&#xff0c;在接到这个任务的时候&#xff0c;本以为很简单&#xff0c;不就是普通的浏览器复制粘贴&#xff0c;最不济找下gpt给生成一下&#xff0c;但是到实际开发就不同了&#xff0c;不是后端出问题就是前端&#xff0c;搜资料&#xff0c;上百度…

【Rust】基本的语法概念

Rust初学习 常见概念变量与可变性变量常量隐藏 数据类型标量类型字符类型复合类型元组数组 函数参数语句和表达式具有返回值的函数 注释控制流使用循环重复执行 常见概念 变量与可变性 变量 fn main() {let x 5;println!("The value of x is: {x}");x 6;println…

AlphaFold的原理及解读

1、背景 蛋白质是生物体内一类重要的生物大分子&#xff0c;其结构复杂多样&#xff0c;蛋白质的结构对于理解其功能和参与的生物学过程具有重要意义。从生物学角度上看&#xff0c;蛋白质的结构可以分为四个层次&#xff1a;初级结构、二级结构、三级结构和四级结构。 初级结…

中英双语大模型ChatGLM论文阅读笔记

论文传送门&#xff1a; [1] GLM: General Language Model Pretraining with Autoregressive Blank Infilling [2] Glm-130b: An open bilingual pre-trained model Github链接&#xff1a; THUDM/ChatGLM-6B 目录 笔记AbstractIntroduction 框架总结1. 模型架构2. 预训练设置3…

MySQL的Linux安装

在MySQL官网下载压缩包MySQL :: Download MySQL Community Server (Archived Versions) 下载完成后将压缩包上传到Linux中。我这里是下的CentOS的压缩包。 并且用的是FinalShell连接工具&#xff0c;可以选择压缩包直接上传。 ​ 上传完毕后&#xff0c;新建mysql文件夹&…

[DASCTF 2023 0X401七月暑期挑战赛] web刷题记录

文章目录 EzFlask方法一 python原型链污染方法二 flask框架静态文件方法三 pin码计算 MyPicDisk方法一 字符串拼接执行命令方法二 phar反序列化 EzFlask 考点&#xff1a;python原型链污染、flask框架理解、pin码计算 源码如下 import uuidfrom flask import Flask, request, …

Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/134561660 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

OpenVINO异步Stable Diffusion推理优化方案

文章目录 Stable Diffusion 推理优化背景技术讲解&#xff1a;异步优化方案思路&#xff1a;异步推理优化原理OpenVINO异步推理Python API同步和异步实现方式对比 oneflow分布式调度优化优势&#xff1a;实现思路 总结&#xff1a; Stable Diffusion 推理优化 背景 2022年&am…

山西电力市场日前价格预测【2023-11-29】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-11-29&#xff09;山西电力市场全天平均日前电价为275.28元/MWh。其中&#xff0c;最高日前电价为415.78元/MWh&#xff0c;预计出现在17:45。最低日前电价为0.00元/MWh&#xff0c;预计出…

Make Pixels Dance: High-Dynamic Video Generation论文解析

高动态视频生成的新进展 Make Pixels Dance: High-Dynamic Video Generation高动态视频生成的新进展前言视频生成模式摘要论文十问实验数据集定量评估指标消融研究 训练和推理技巧训练技术推理技术 更多的应用 Make Pixels Dance: High-Dynamic Video Generation 高动态视频生…

MySQL在Docker容器中的性能损失分析与优化策略

文章目录 1. Docker容器对MySQL性能的潜在影响1.1. IO性能1.2. 网络性能1.3. 资源隔离 2. 优化策略2.1. 使用本地数据卷2.2. 配置合理的容器网络2.3. 限制容器资源2.4. 使用容器编排工具 3. 性能测试与监控4. 结论 &#x1f389;MySQL在Docker容器中的性能损失分析与优化策略 ☆…

sqli-labs靶场详解(less17-less22)

目录 less-17 less-18 less-19 less-20 less-21 less-22 less-17 修改密码关卡 服务器后端 账号密码都存在数据库中 使用UPDATE进行修改密码 尝试username处 尝试好久尝试不出来应该是对用户名进行了过滤 于是对password进行注入 判断注入点 passwdadmin 报错&#xff1a…

MySQL使用函数和存储过程实现:向数据表快速插入大量测试数据

实现过程 1.创建表 CREATE TABLE user_info (id INT(11) NOT NULL AUTO_INCREMENT,name VARCHAR(20) DEFAULT NULL,age INT(3) DEFAULT NULL,pwd VARCHAR(20) DEFAULT NULL,phone_number VARCHAR(11) DEFAULT NULL,email VARCHAR(255) DEFAULT NULL,address VARCHAR(255) DEF…

【TinyALSA全解析(二)】wav和pcm音频文件格式详解

wav和pcm音频文件格式详解 一、本文的目的二、wav和pcm格式文件介绍三、pcm格式文件解析四、wav文件内容解析4.1 文件内容描述4.2 实战分析 五、如何在各种音频格式之间进行转换 /******************************************************************************************…

技术SEO的基础知识和 10 个最佳实践

你有没有想过导致某些网站在搜索结果中排名比其他网站更好的因素&#xff1f;针对搜索引擎进行优化是关键&#xff08;SEO&#xff09;。SEO&#xff0c;即搜索引擎优化&#xff0c;是一种用于提高网站在搜索引擎中的知名度的方法。技术搜索引擎优化&#xff08;SEO&#xff09…

用CHAT总结费曼学习法的关键

问CHAT&#xff1a;费曼学习法的关键 CHAT回复&#xff1a;费曼学习法是由著名物理学家理查德费曼所发明的一种学习方法&#xff0c;旨在以深入理解为目标&#xff0c;帮助自己学习新的知识和技能。 费曼学习法有四个关键步骤&#xff1a; 1. 学习&#xff1a;首先&#xff0…