Java Stream流的使用

Java Stream流的使用,第1张

Java Stream流的使用

Stream流——Java8新特性之一

用于处理集合,它可以指定你希望对集合进行的 *** 作,可以执行非常复杂的查找、过滤和映射数据等 *** 作。

本文只给出方法以及其用法,可作为工具书来使用。

对Stream的理解、研究与总结可参考这篇文章:——(待整理)——

进入正题。

使用到的实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
class Student {
    private String name;
    private int age;
}

@Data、@AllArgsConstructor、@NoArgsConstructor三个注解是在maven里面引用了lombok,作用是帮我们创建了get、set、有参与无参构造方法,也可以自己手动创建。

流的创建:

// 1、通过 Collection.stream() 方法用集合创建流
List list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream stream = list.stream();
// 创建一个并行流
Stream parallelStream = list.parallelStream();
Stream parallelStream2 = list.stream().parallel();

// 2、使用 Arrays.stream(T[] array) 方法用数组创建流
int[] array={1,3,5,6,8};
IntStream intStream = Arrays.stream(array);

// 3、使用Stream的静态方法:of()、iterate()、generate()
Stream stream1 = Stream.of(1, 2, 3, 4, 5, 6);
stream1.forEach(System.out::println);
Stream stream2 = Stream.iterate(0, (x) -> x + 3).limit(10);
stream2.forEach(System.out::println);
Stream stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

filter:筛选

List list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
Stream stream = list.stream();
stream.filter(x -> x > 7).forEach(System.out::println);

concat:合并

String[] arr1 = {"a", "b", "c", "d"};
String[] arr2 = {"d", "e", "f", "g"};
Stream stream1 = Stream.of(arr1);
Stream stream2 = Stream.of(arr2);
List newList = Stream.concat(stream1, stream2).collect(Collectors.toList());
System.out.println("流合并:" + newList);// 流合并:[a, b, c, d, d, e, f, g]

distinct:去重

Integer[] arr = {1, 2, 2, 3, 4, 4, 5};
List list = Arrays.stream(arr).distinct().collect(Collectors.toList());
System.out.println("流去重:" + list);// 流去重:[1, 2, 3, 4, 5]

skip:跳过前n个数据

List collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
System.out.println("skip:" + collect2);// skip:[3, 5, 7, 9, 11]

limit:获取前n个元素

List collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
System.out.println("limit:" + collect);// limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

skip和limit加起来可以实现分页功能。

map:映射,函数会被应用到每个元素上,并将其映射成一个新的元素。

String[] strArr = {"rety", "fghfg", "terd", "nye"};
List strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
System.out.println("每个元素大写:" + strList);// 每个元素大写:[RETY, FGHFG, TERD, NYE]

//将每个元素转成一个新的且不带逗号的元素
List list = Arrays.asList("h,y,g,g,e", "3,6,9,12");
List listNew = list.stream().map(s -> s.replaceAll(",", "")).collect(Collectors.toList());
System.out.println("去掉逗号:" + listNew);// 去掉逗号:[hygge, 36912]

List intList = Arrays.asList(2, 4, 5, 8, 10);
List intListNew = intList.stream().map(x -> x + 2).collect(Collectors.toList());
System.out.println("每个元素+3:" + intListNew);// 每个元素+3:[4, 6, 7, 10, 12]

flatMap:映射,流中的每个值都换成另一个流,然后把所有流连接成一个流。

List list = Arrays.asList("h,y,g,g,e", "3,6,9,12");
List listNew = list.stream().flatMap(s -> {
    //将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream s2 = Arrays.stream(split);
    return s2;
}).collect(Collectors.toList());
System.out.println("处理前:元素个数为 " + list.size() + " 个,集合为 " + list);
System.out.println("处理后:元素个数为 " + listNew.size() + " 个,集合为 " + listNew);


这里注意虽然他们的集合打印出来很相似,但是注意他们的元素个数是不一样的。

sorted:排序

// 默认排序:即无参时
List list = Arrays.asList(5, 6, 4);
list.stream().sorted().forEach(System.out::println);
// 自定义排序
List studentList = new ArrayList<>();
studentList.add(new Student("A", 10));
studentList.add(new Student("B", 20));
studentList.add(new Student("A", 30));
studentList.add(new Student("D", 40));
// 按年龄升序
studentList.stream().sorted(Comparator.comparingInt(Student::getAge)).forEach(System.out::println);
// 先按姓名升序,姓名相同则按年龄升序
studentList.stream().sorted(
        (o1, o2) -> {
            if (o1.getName().equals(o2.getName())) {
                return o1.getAge() - o2.getAge();
            } else {
                return o1.getName().compareTo(o2.getName());
            }
        }
).forEach(System.out::println);
// 先按姓名升序,姓名相同则按年龄升序,更简单的写法  
studentList.stream().sorted(Comparator.comparing(Student::getName).thenComparing(Student::getAge)).forEach(System.out::println);

peek:消费

List studentList = new ArrayList<>();
studentList.add(new Student("A", 10));
studentList.add(new Student("B", 20));
System.out.println("原始数据:" + studentList);

// 使用map对比
List result = studentList.stream().map(o -> {
    o.setAge(50);
    return o;
}).collect(Collectors.toList());
System.out.println("使用map修改数据:" + result);

// 使用peek
List result1 = studentList.stream().peek(o -> o.setAge(100)).collect(Collectors.toList());
System.out.println("使用peek修改数据:" + result1);


这里专门使用了map与peek做了个对比,你会发现他们都改变了age的值,而peek区别与map,它不需要返回值。但是map能改变数据类型,peek就做不到。

所以peek 可以做一些打印或者修改工作。

源码注释中作者给的例子也证明了这种想法:

Stream.of("one", "two", "three", "four")
    .filter(e -> e.length() > 3)
    .peek(e -> System.out.println("Filtered value: " + e))
    .map(String::toUpperCase)
    .peek(e -> System.out.println("Mapped value: " + e))
    .collect(Collectors.toList());

对于map与peek的具体分析看这篇文章:——(待整理)——

max:返回流中元素的最大值

List list = Arrays.asList("admn", "bgfmt", "pbtd", "xbafdd", "weoufgsd");
Optional max = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:" + max.get());// 最长的字符串:weoufgsd

List intlist = Arrays.asList(7, 6, 9, 4, 11, 6);
Optional max2 = intlist.stream().max(Integer::compareTo);
System.out.println("自然排序的最大值:" + max2.get());// 自然排序的最大值:11

min:返回流中元素的最小值

List list = Arrays.asList("admn", "bgfmt", "pbtd", "xbafdd", "weoufgsd");
Optional min = list.stream().min(Comparator.comparing(String::length));
System.out.println("最短的字符串:" + min.get());// 最短的字符串:admn

List intList = Arrays.asList(7, 6, 9, 4, 11, 6);
Optional min2 = intList.stream().min(Integer::compareTo);
System.out.println("自然排序的最小值:" + min2.get());// 自然排序的最小值:4

count:返回流中元素的总个数

List list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
long count = list.stream().count();
System.out.println("list的元素总个数:" + count);// list的元素总个数:7

long countOfGt6 = list.stream().filter(x -> x > 6).count();
System.out.println("list中大于6的元素个数:" + countOfGt6);// list中大于6的元素个数:4

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false

List list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
boolean allMatch = list.stream().allMatch(x -> x > 6);
System.out.println("是否全部值都大于6:" + allMatch);// 是否全部值都大于6:false

noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false

List list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
boolean noneMatch = list.stream().noneMatch(x -> x > 6);
System.out.println("是否不存在大于6的值:" + noneMatch);// 是否不存在大于6的值:false

anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false

List list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
boolean anyMatch = list.stream().anyMatch(x -> x > 6);
System.out.println("是否存在大于6的值:" + anyMatch);// 是否存在大于6的值:true

findFirst:返回流中第一个元素

List list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
Optional findFirst = list.stream().findFirst();
System.out.println("list中的第一个数:"+findFirst.get());// list中的第一个数:7

Optional findFirstGt7 = list.stream().filter(x -> x > 7).findFirst();
System.out.println("list中第一个大于7的数:"+findFirstGt7.get());// list中第一个大于7的数:9

findAny:返回流中的任意元素

// 适用于并行流,源码有注释:This is to allow for maximal performance in parallel operations
List list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
Optional findAny = list.parallelStream().findAny();
System.out.println("匹配任意一个值:" + findAny.get());// 匹配任意一个值:8

Optional findAnyGt5 = list.parallelStream().filter(x -> x > 5).findAny();
System.out.println("匹配任意一个大于5的值:" + findAnyGt5.get());// 匹配任意一个大于5的值:8

PS:使用上面代码自己测试的时候,得多次运行才可能得到不一样的返回值,可以试着改变list的长度或者类型来增加返回不同值的概率。

reduce:归约

例子中也可以使用(x, y) -> x > y ? x : y 替换 Integer::max

List list = Arrays.asList(1, 3, 2, 8, 11, 4);
        
// 一个参数:Optional reduce(BinaryOperator accumulator)
Optional sum = list.stream().reduce(Integer::sum);
Optional product = list.stream().reduce((x, y) -> x * y);
Optional max = list.stream().reduce(Integer::max);
System.out.println("list求和:" + sum.get()+ ",求积:" + product.get()+",最大值:" + max.get());// list求和:29,求积:2112,最大值:11

// 两个参数:T reduce(T identity, BinaryOperator accumulator)
Integer sum2 = list.stream().reduce(0, Integer::sum);
Integer product2 = list.stream().reduce(1, (x, y) -> x * y);
Integer max2 = list.stream().reduce(0, Integer::max);
System.out.println("list求和:" + sum2+ ",求积:" + product2+",最大值:" + max2);// list求和:29,求积:2112,最大值:11

//  U reduce(U identity,BiFunction accumulator,BinaryOperator combiner)
Integer sum3 = list.parallelStream().reduce(0, Integer::sum, Integer::sum);
Integer product3 = list.stream().reduce(1, (x, y) -> x * y, (x, y) -> x * y);
Integer max3 = list.stream().reduce(0, Integer::max, Integer::max);
System.out.println("list求和:" + sum3+ ",求积:" + product3+",最大值:" + max3);// list求和:29,求积:2112,最大值:11

reduce三种不同方式的分析,请看这篇文章:——(待整理)——

collect:归集 (Collectors:toList/toSet/toMap)

List students = new ArrayList<>();
students.add(new Student("A", 10));
students.add(new Student("B", 20));
students.add(new Student("C", 10));
// 转成list:toList
List ageList = students.stream().map(Student::getAge).collect(Collectors.toList());
System.out.println("ageList:" + ageList);// ageList:[10, 20, 10]
// 转成Set:toSet
Set ageSet = students.stream().map(Student::getAge).collect(Collectors.toSet());
System.out.println("ageSet:" + ageSet);// ageSet:[20, 10]
// 类似Set:distinct去重
List ageSet1 =  students.stream().map(Student::getAge).distinct().collect(Collectors.toList());
System.out.println("ageSet1:" + ageSet1);// ageSet1:[10, 20]
// 转成Map:toMap
Map studentMap = students.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
System.out.println("studentMap:" + studentMap);// studentMap:{A=10, B=20, C=10}

使用collect(Collectors.toSet())与distinct().collect(Collectors.toList()),最终都能得到一个包含不重复元素的集合,但是你会发现他们元素的顺序不一样,可以大胆的猜测他们写入的顺序不一样,具体分析请看文章:——(待整理)——

上面基本上列举出了常用的方法,面对实际问题,我们使用Stream时,可以采用非常多的方式来达到目的。下面我们用一个例子以及一些问题,尽可能的给出多种解决方案,来让大家来熟悉这些方法。

用到的实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
    private String name;  // 姓名
    private int salary;   // 薪资
    private int age; 	  // 年龄
    private String sex;   // 性别
    private String area;  // 地区
}

实例化:

List personList = new ArrayList();
personList.add(new Person("张三", 39000, 23, "男", "北京"));
personList.add(new Person("王五", 20000, 25, "男", "上海"));
personList.add(new Person("李四", 28000, 21, "女", "上海"));
personList.add(new Person("小明", 32000, 24, "女", "北京"));
personList.add(new Person("小刚", 45000, 25, "男", "北京"));
personList.add(new Person("小红", 29000, 26, "女", "上海"));

1、工资大于28000的人员列表

Map map = personList.stream().filter(p -> p.getSalary() > 28000).collect(Collectors.toMap(Person::getName, Person::getSalary));
System.out.println("高于28000的员工姓名:" + map.keySet());
        
List List = personList.stream().filter(x -> x.getSalary() > 28000).map(Person::getName).collect(Collectors.toList());
System.out.println("高于28000的员工姓名:" + List);


2、改变员工信息

// 不改变原来员工集合的方式
List personListNew = personList.stream().map(person -> {
    Person personNew = new Person(person.getName(), 0, 0, null, null);
    personNew.setSalary(person.getSalary() + 10000);
    return personNew;
}).collect(Collectors.toList());
System.out.println("一次改动前:" + personList);
System.out.println("一次改动后:" + personListNew);

// 改变原来员工集合的方式
List personListNew2 = personList.stream().map(person -> {
    person.setSalary(person.getSalary() + 10000);
    return person;
}).collect(Collectors.toList());
System.out.println("二次改动前:" + personList);
System.out.println("二次改动后:" + personListNew2);

3、求工资之和方式:

Optional sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);

Integer sumSalary2 = personList.stream().map(Person::getSalary).reduce(0, Integer::sum);

Integer sumSalary3 = personList.parallelStream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);

OptionalInt sumSalary4 = personList.stream().mapToInt(Person::getSalary).reduce(Integer::sum);

int sumSalary5 = personList.stream().mapToInt(Person::getSalary).sum();

Integer sumSalary6 = personList.stream().collect(Collectors.summingInt(Person::getSalary));

System.out.println("工资之和:" + sumSalary.get() + "," + sumSalary2 + "," + sumSalary3 + "," +
 sumSalary4.getAsInt() + "," + sumSalary5 + "," + sumSalary6);


4、求最高工资方式:

Optional maxSalary = personList.stream().map(Person::getSalary).reduce((max1, max2) -> max1 > max2 ? max1 : max2);

Optional maxSalary2 = personList.stream().map(Person::getSalary).reduce(Integer::max);

Integer maxSalary3 = personList.stream().map(Person::getSalary).reduce(0, Integer::max);

Integer maxSalary4 = personList.parallelStream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(), Integer::max);

Optional maxSalary5 = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));

Optional maxSalary6 = personList.stream().map(Person::getSalary).max(Integer::compare);

Optional maxSalary7 = personList.stream().max(Comparator.comparingInt(Person::getSalary));

System.out.println("最高工资:" + maxSalary.get() + "," + maxSalary2.get() + "," + maxSalary3 + ","
        + maxSalary4 + "," + maxSalary5.get() + "," + maxSalary6.get() + "," + maxSalary7.get().getSalary());



5、求平均工资

Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));

Double average2 = personList.stream().mapToDouble(Person::getSalary).sum() / personList.size();

System.out.println("员工平均工资:" + average + "," + average2);


6、求人员总数

long count = personList.stream().collect(Collectors.counting());

long count2 = personList.stream().count();

long count3 = personList.size();

System.out.println("员工总数:" + count + "," + count2 + "," + count3);


7、一次性统计所有信息

DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
System.out.println("员工工资所有统计:" + collect);


8、分组

// 将员工按薪资是否高于8000分组
Map> groupBySalary = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
System.out.println("按员工薪资是否大于8000分组情况:" + groupBySalary);

// 将员工按性别分组
Map> groupBySex = personList.stream().collect(Collectors.groupingBy(Person::getSex));
System.out.println("按员工性别分组情况:" + groupBySex);

// 将员工先按性别分组,再按地区分组
Map>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
System.out.println("按员工性别、地区:" + group);

9、排序

// 按工资升序排序(自然排序)
List newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
        .collect(Collectors.toList());
System.out.println("按工资升序排序:" + newList);

// 按工资倒序排序
List newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
        .map(Person::getName).collect(Collectors.toList());
System.out.println("按工资降序排序:" + newList2);

// 先按工资再按年龄升序排序
List newList3 = personList.stream()
        .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
        .collect(Collectors.toList());
System.out.println("先按工资再按年龄升序排序:" + newList3);

// 先按工资再按年龄自定义排序(降序)
List newList4 = personList.stream().sorted((p1, p2) -> {
    if (p1.getSalary() == p2.getSalary()) {
        return p2.getAge() - p1.getAge();
    } else {
        return p2.getSalary() - p1.getSalary();
    }
}).map(Person::getName).collect(Collectors.toList());
System.out.println("先按工资再按年龄自定义降序排序:" + newList4);



上面有些方法其实使用并不恰当,在这里只是起到了抛砖引玉的作用,大家选取最适用的的即可。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5686318.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)