java Stream流操作

发布时间 2023-06-30 16:47:39作者: 风筝上的猫

什么是Stream?

java8新增Stream,配合同版出现的Lambda,为集合(Collection)操作提供了极大的便利。
Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
 
0
Strem可以由数组或集合创建,对流的操作由中间操作和终端操作两种。
  中间操作是在数据源上进行的转换操作,每次操作都会返回一个新的Stream实例,以便继续进行操作。
  终端操作是指对Stream进行最终操作的操作,如收集、计算或迭代等。
1)中间操作,可以有多个,每次返回一个新的流,可进行链式操作。
2)终端操作,只能有一个,每次执行完,这个流就结束了,因此只能放在最后。
 

Stream特点:

1、Stream流不是集合元素,不是数据结构且并不保存数据,主要目的在于计算。
2、代码简洁,函数式编程写出来的代码简洁、意图明确
3、stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
4、stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
 

常见的Stream操作

  • 遍历

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);

// 遍历输出符合条件的元素
list.stream().filter(x -> x > 6).forEach(System.out::println); //7 8 9
// 匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
// 匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
// 是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(x -> x > 6);
System.out.println("匹配第一个值:" + findFirst.get()); //7
System.out.println("匹配任意一个值:" + findAny.get()); //8
System.out.println("是否存在大于6的值:" + anyMatch); //true


List<String> names = Arrays.asList("John", "Jane", "Mike");
names.stream().forEach(name -> System.out.println("Hello, " + name));
//Hello, John
//Hello, Jane
//Hello, Mike
  • 映射

map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个
/** 
 *例一
 *使用stream()方法将列表num转换为一个Stream对象。接着,使用map()方法将每个元素映射为它的平方,
 *并得到新的Stream对象。最后,我们使用collect()方法将映射后的结果收集到一个List中
**/
List<Integer> num = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = num.stream().map(n -> n * n).collect(Collectors.toList());
System.out.println(squares);  //[1,4,9,16,25]



/**
 *例二
 *英文字符串数组的元素全部改为大写。整数数组每个元素+3。
**/
String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());

List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());

System.out.println("每个元素大写:" + strList);  //[ABCD,BCDD,DEFDE,FTR]
System.out.println("每个元素+3:" + intListNew);  //[4,6,8,10,12,14]


/**
 *例三
 *将员工的薪资全部增加1000。
**/
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
personList.add(new Person("Owen", 9500, 25, "male", "New York"));
personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

// 不改变原来员工集合的方式
List<Person> 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.get(0).getName() + "-->" + personList.get(0).getSalary());
System.out.println("一次改动后:" + personListNew.get(0).getName() + "-->" + personListNew.get(0).getSalary());

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



/**
 *例四
 *将两个字符数组合并成一个新的字符数组。
**/
List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
List<String> listNew = list.stream().flatMap(s -> {
    // 将每个元素转换成一个stream
    String[] split = s.split(",");
    System.out.println("split长度:"+split.length);
    System.out.println("s长度:"+s.length());
    System.out.println("s内元素:"+s.toString());
    Stream<String> s2 = Arrays.stream(split);
    return s2;
}).collect(Collectors.toList());

System.out.println("处理前的集合:" + list);
System.out.println("处理后的集合:" + listNew);
 
0
 
  • 过滤

/**
 *使用filter()方法来过滤出长度大于5的元素,得到了新的Stream对象。最后,使用collect()方法将过滤
 *后的结果收集到一个List中
**/
List<String> words = Arrays.asList("apple", "banana", "orange", "pear", "grape");
List<String> longWords = words.stream().filter(word -> word.length() > 5).collect(Collectors.toList());
System.out.println(longWords);
// [banana, orange]
 
  • 排序

/**
 *使用stream()方法将列表转换为一个Stream对象。接着,使用sorted()方法将元素按字典序排序,并得
 *到新的Stream对象。最后,使用collect()方法将排序后的结果收集到一个List中,
**/
List<String> words = Arrays.asList("apple", "banana", "orange", "pear", "grape");
List<String> sortedWords = words.stream().sorted().collect(Collectors.toList());
System.out.println(sortedWords);
// [apple, banana, grape, orange, pear]

 

  • 去重

/**
 *除去重复元素
 **/
List<String> names = Arrays.asList("John", "Jane", "John", "Mike", "Mike");
List<String> distinctNames = names.stream().distinct().collect(Collectors.toList());
System.out.println(distinctNames);// 输出:[John, Jane, Mike]
 
  • 统计操作(count、min、max、sum、average)

List<Integer> numbers = Arrays.asList(2, 4, 6, 8, 10);
long count = numbers.stream().count();
int max = numbers.stream().max(Integer::compareTo).orElse(0);
int min = numbers.stream().min(Integer::compareTo).orElse(0);
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
double average = numbers.stream().mapToDouble(Integer::intValue).average().orElse(0.0);
System.out.println("Count: " + count); // 输出:Count: 5
System.out.println("Max: " + max); // 输出:Max: 10
System.out.println("Min: " + min); // 输出:Min: 2
System.out.println("Sum: " + sum); // 输出:Sum: 30
System.out.println("Average: " + average); // 输出:Average: 6.0
compareTo()会返回数字,如果两个字符串内容相同,会返回0,字符串a大于字符串b,会返回相差的ASCII码的正数,字符串a小于字符串b,会返回相差的ASCII码的负数。
在Java8中,orElse方法是0ptional类中的一个方法,它的作用是当Optional对象中的值为空时,返回一个指定的默认值。
举个例子,假设有一个0ptional对象,它可能包含一个字符串值,也可能是空的。如果我们想要获取这个字符串值,但又不确定它是否为nuli,就可以使用orElse方法。
  例如:
  0ptional optional = 0ptional.ofNullable(str);String result = optional.orElse('default') ;
  这段代码首先创建了一个0ptional对象,其中可能包含一个字符串值。然后我们调用orElse方法,如果这个Optional对象中的值为空,就会返回一个指定的默认值'default',否则就返回0ptional对象中的值。
/**
 *统计员工人数、平均工资、工资总额、最高工资。
**/
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));

// 求总数
Long count = personList.stream().collect(Collectors.counting());
// 求平均工资
Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
// 求最高工资
Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
// 求工资之和
Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
// 一次性统计所有信息
DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));

System.out.println("员工总数:" + count);  //3
System.out.println("员工平均工资:" + average);  //7900
System.out.println("员工工资总和:" + sum);  //23700
System.out.println("员工工资所有统计:" + collect); //DoubleSummaryStatistics{count=3,sum=23700.000009,min=700.880000, average=7900.000808,max=8900.000000}
/**
 *使用了collect()方法将列表中的元素连接成一个字符串,使用joining()方法指定连接符。
**/
List<String> names = Arrays.asList("John", "Jane", "Mike");
System.out.println(names); //输出:[John, Jane, Mike]
String joinedNames = names.stream().collect(Collectors.joining(", "));
System.out.println(joinedNames); // 输出:John, Jane, Mike


/**
 *Collectors类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors常用于返回列表或字符串
**/
.collect(Collectors.toList()) // 返回列表
.collect(Collectors.joining(", ") // 返回字符串

因为流不存储数据,在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。常用方法有toList、toSet和toMap。

List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
List<Integer> listNew = list.stream().map(x -> x * x ).collect(Collectors.toList());
Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());

System.out.println("toList:" + listNew); //[1, 36, 9, 16, 36, 49, 81, 36, 400]
System.out.println("toSet:" + set); //[4,20,6]