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

目录

一. 前言

二. 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Zookeeper 实战 | Zookeeper 和Spring Cloud相结合解决分布式锁、服务注册与发现、配置管理

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

HarmonyOS-Service服务开发(一)

文章目录 创建新项目启动Serviceets获取service的bundleName DataAbility开发指导开发Data步骤创建Data 创建新项目 ServiceAbility开发指导 在config.json中也有配置出现 启动Service ets获取service的bundleName 项目的bundleName service的bundleName 这里serviceAbil…

青少年CTF之PHP特性练习(1-5)

青少年CTF-PHP特性练习 文章目录 青少年CTF-PHP特性练习PHP特性01PHP特性02PHP特性03PHP特性04PHP特性05 PHP特性01 看给出的源码&#xff0c;两个变量的值加密后的MD5相同 <?php$s1 "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47…

Nacos整合实际应用案例

Nacos数据隔离模型 公司->命名空间->分组->服务 命名空间通常用于隔离不同微服务之间的配置 分组用于隔离相同微服务下不同环境的配置 版本对应关系 https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 应用案例 <par…

easyExcel自定义导出,指定列,设置请求头背景色,加入合计行,设置合计行字体,背景色等等

效果图 1.引入easyExcel pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.1</version></dependency> 2.工具类-自定义样式handler-CustomCellWriteHandler import java.util…

GAN:DCGAN-深度卷积生成对抗网络

论文&#xff1a;https://arxiv.org/pdf/1511.06434.pdf 发表&#xff1a;ICLR 2016 一、架构创新 1&#xff1a;全卷积网络&#xff1a;用逐步卷积代替确定性的空间池化函数&#xff08;如maxpooling&#xff09;&#xff0c;使网络学习自己的空间下采样。使用这种方法&#…

【文献阅读笔记】关于GANomaly的异常检测方法

文章目录 1、GANomaly: Semi-Supervised Anomaly Detection via Adversarial Training模型主要创新 2、Skip-GANomaly: Skip Connected and AdversariallyTrained Encoder-Decoder Anomaly Detection模型主要创新点 3、Industrial surface defect detection and localization u…

15 网关实战: 微服务集成Swagger实现在线文档

上节介绍了网关层面聚合API文档,通过网关的路由信息找到了各个服务的请求地址,这节讲一下微服务如何集成Swagger。 网关的API文档默认调用的是微服务的**/v2/api-docs**这个接口获取API详细信息,比如文章服务的URL:http://localhost:9000/blog-article/v2/api-docs,返回信…

【DeepLearning.AI】吴恩达系列课程——使用Gradio构建AI应用

目录 前言一、Gradio介绍1-1、Gradio介绍1-2、安装1-3、小栗子 二、使用Gradio构建AI应用2-1、NLP任务2-1-1、文本摘要2-1-2、命名实体识别 2-2、聊天任务&#xff08;ChatYuan&#xff09;2-2-1、模型介绍2-2-2、模型下载、参数设置2-2-3、模型测试2-2-4、嵌入到Gradio里2-2-5…

leetcode:2864. 最大二进制奇数(python3解法)

难度&#xff1a;简单 给你一个 二进制 字符串 s &#xff0c;其中至少包含一个 1 。 你必须按某种方式 重新排列 字符串中的位&#xff0c;使得到的二进制数字是可以由该组合生成的 最大二进制奇数 。 以字符串形式&#xff0c;表示并返回可以由给定组合生成的最大二进制奇数。…

SVN代码回滚之Update item to thisversion和Revert to this version 区别

背景 在使用SVN管理代码时免不了进行代码的合并或者回退&#xff0c;本篇主要讲回退至某个版本的SVN操作。 内容 对目标代码右键查看log&#xff0c;选中某个你想回滚的版本&#xff0c;然后右键会看到如下图 假设log中已经有5个版本&#xff0c;分别为1&#xff0c;2&…

Phpstudy v8.0/8.1小皮升级Apache至最新,同时升级openssl版本httpd-2.4.58 apache 2.4.58

1.apache官网下载最新版本的apache 2.4.58 2.phpstudy下apache停止运行&#xff0c;把原来的Apache文件夹备份一份 复制图中的文件替换apache目录下文件 3.phpstudy中开启apache

用按层次顺序遍历二叉树的方法,设计算法统计树中度为1的结点数目

用按层次顺序遍历二叉树的方法&#xff0c;设计算法统计树中度为1的结点数目 代码思路&#xff1a; 层序遍历的实现需要借助一个辅助队列 首先将根结点入队&#xff0c;然后根出队&#xff0c;把根的两个子树入队 然后下面循环执行&#xff1a;队头元素出队&#xff0c;队头元…

手机文件怎么传到电脑?简单方法分享!

将手机文件传输到电脑可以将其备份&#xff0c;以防数据丢失或意外情况发生。并且电脑具有更强大的处理能力&#xff0c;可以将文件进行编辑、修改、转换等操作&#xff0c;大大提高了工作效率。那么&#xff0c;手机文件怎么传到电脑&#xff1f;本文将为大家提供简单易懂的解…

[SpringCloud] SpringCloud配置中心的核心原理

SpringCloud是什么时候去拉取配置中心的配置中心客户端的配置信息为什么要写在bootstrap文件中对象中注入的属性是如何动态刷新的一些开源的配置中心是如何整合SpringCloud的 文章目录 1.从SpringBoot的启动过程说起1.1 大致过程 2.准备Environment的核心操作2.1 前置操作 3.pr…

Redis 两种持久化方式 AOF 和 RDB

目录 一、Redis 的持久化 二、Redis 的持久化方式 RDB RDB 介绍 RDB 的触发方式&#xff1a;. 三、RDB的文件生成策略 四、Save 和 Bgsave 命令的区别 六、RDB 最佳配置 七、触发机制-不容忽略方式 AOF 一、AOF介绍 二、RDB所存在的问题 三、AOF 三种策略 四、AOF…

6.15合并二叉树(LC617-E)

算法&#xff1a; 前序、中序、后序都可以&#xff0c;这道题正常逻辑一般都是用前序 正确代码&#xff1a; 这里就是在root1这颗树上改的&#xff0c;也可以新建一个树。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode …

Long-Context下LLM模型架构全面介绍

深度学习自然语言处理 原创作者&#xff1a;cola 随着ChatGPT的快速发展&#xff0c;基于Transformer的大型语言模型(LLM)为人工通用智能(AGI)铺平了一条革命性的道路&#xff0c;并已应用于知识库、人机界面和动态代理等不同领域。然而&#xff0c;存在一个普遍的限制:当前许多…

「Verilog学习笔记」非整数倍数据位宽转换8to12

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 要实现8bit数据至12bit数据的位宽转换&#xff0c;必须要用寄存器将先到达的数据进行缓存。8bit数据至12bit数据&#xff0c;相当于1.5个输入数据拼接成一个输出数据&#…

【1】AR Tag 在ros中的使用

1.定义 AR Tag 是一种用于增强现实&#xff08;AR&#xff09;应用中的视觉标记&#xff0c;用于跟踪和定位虚拟物体在现实世界中的位置。 AR Tag由黑白正方形图像表示&#xff0c;图像内部有黑色边框中的某些图案。它与我们经常用到的二维码长得类似&#xff0c;原理其实也一…