Steam流操作

生成流

通过集合生成

// list 列表转换为 Stream
List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("b");
Stream stream3 = strList.stream();
stream3.forEach(System.out::println);
System.out.println("=====================");

// Set 集合转换为 Stream
Set<String> strSet = new HashSet<>();
strSet.add("a");
strSet.add("b");
Stream stream4 = strSet.stream();
stream4.forEach(System.out::println);
System.out.println("=====================");

// Map 集合转换为 Stream
Map<String, Integer> map = new HashMap<>();
map.put("a"100);
map.put("b"200);
Stream stream5 = map.entrySet().stream();
stream5.forEach(System.out::println);
System.out.println("=====================");

通过数组生成

int[] intArr = new int[]{12345};
IntStream stream = Arrays.stream(intArr);

该方法生成的流是数值流(IntStream)而不是 Stream,使用数值流可以避免计算过程中拆箱装箱,提高性能

通过值生成

Stream<Integer> stream = Stream.of(12345);

通过 Stream 的 of 方法生成流,通过 Stream 的 empty 方法可以生成一个空流

通过文件生成

Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());

通过 Files.line 方法得到一个流,并且得到的每个流是给定文件中的一行

通过函数生成

  1. iterate
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);

iterate 方法接受两个参数,第一个为初始值,第二个为进行的函数操作,因为 iterate 生成的流为无限流,通过 limit 方法对流进行了截断,只生成 5 个偶数

  1. generate
Stream<Double> stream = Stream.generate(Math::random).limit(5);

generate 方法接受一个参数,方法参数类型为 Supplier,由他为流提供值,generate 生成的流也是无限流,因此通过 limit 对流进行了截断

中间操作

filter 条件筛选

返回结果生成新的流中只包含满足筛选条件的数据

List<Integer> integerList = Arrays.asList(112345);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);

distinct 去除重复元素

List<Integer> integerList = Arrays.asList(112345);
Stream<Integer> stream = integerList.stream().distinct();

limit 返回指定流个数

List<Integer> integerList = Arrays.asList(112345); 
Stream<Integer> stream = integerList.stream().limit(3);

通过 limit 方法指定返回流的个数,limit 的参数值必须 >= 0,否则将会抛出异常

skip 跳过流中的元素

List<Integer> integerList = Arrays.asList(112345);
Stream<Integer> stream = integerList.stream().skip(2);

通过 skip 方法跳过流中的元素,skip 的参数值必须 >= 0,否则将会抛出异常

map 流映射

流映射就是将接受的元素映射成另外一个元素,通过 map 方法可以完成映射

List<String> stringList = Arrays.asList("Java 8""Lambdas",  "In""Action");
Stream<Integer> stream = stringList.stream().map(String::length);

常用方法

  1. mapToDouble
  2. mapToInt
  3. mapToLong

使用案例

  1. 当出现相同的 key 时,解决方法:取前面 value 的值,或者取后面放入的 value 值,则覆盖先前的 value 值
Map<Long, String> map = userList.stream()
        .collect(Collectors.toMap(User::getId, User::getUsername, (v1, v2) -> v1));
Map<Long, String> map = userList.stream()
        .collect(Collectors.toMap(User::getId, User::getUsername, (v1, v2) -> v2));
  1. 对相同 key 值的数据进行合并
// 处理 itemList 合并相同物料累加数量
Map<Long, Integer> map = itemList.stream().collect(Collectors.toMap(StocksComponentsItem::getStocksId, StocksComponentsItem::getCount, (e1, e2) -> e1 + e2));       
  1. 获取 TreeMap,根据 key 值进行排序
Map<Long, String> treeMap = new HashMap<>();
TreeMap<Long, String> map = treeMap.entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue(), 
                          (v1, v2) -> v1, TreeMap::new));

flatMap 流转换

扁平化映射,将多个 stream 连接成一个 stream,这个操作是针对类似多维数组的,比如集合里面包含集合,相当于降维作用

例如:如果想要从 List < StdCls > 中取出学生列表,需要取出每个班级的学生 List,再 for 循环调用 List.addAll()方法把所有班级的学生 List 到一个新的总和 List 中

@Data
public class StdCls {
    private String clsNo;
    private List<Student> studentList;
}

使用 map 映射后会变成 List <List<Student> >,若使用 flatMap 会进行扁平化处理,从而将 List 嵌套 List 用 stream 合并成一个 List

List<String> nameList = stdClsList.stream()
                        .map(StdCls::getStudentList)
                        .flatMap(Collection::stream) // 多个集合 List 合并
                        .map(Student::getName).collect(Collectors.toList());

sorted 排序

将流中的元素按照自然排序方式进行排序

  1. sorted():自然排序,流中元素需实现 Comparable 接口
List<String> list = Arrays.asList("aa""ff""dd");
//String 类自身已实现 Compareable 接口
list.stream().sorted().forEach(System.out::println);// aa dd ff
  1. sorted(Comparator com):定制排序,自定义 Comparator 排序器
Student s1 = new Student("aa"10);
Student s2 = new Student("bb"20);
Student s3 = new Student("aa"30);
Student s4 = new Student("dd"40);
List<Student> studentList = Arrays.asList(s1, s2, s3, s4);
  
//自定义排序:先按姓名升序,姓名相同则按年龄升序
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); 

peek 消费

对流中每个元素执行操作,并返回一个新的流,返回的流还是包含原来流中的元素。

如同于 map,能得到流中的每一个元素,但 map 接收的是一个 Function 表达式,有返回值;而 peek 接收的是 Consumer 表达式,没有返回值

Student s1 = new Student("aa"10);
Student s2 = new Student("bb"20);
List<Student> studentList = Arrays.asList(s1, s2);
  
studentList.stream().peek(o -> o.setAge(100)).forEach(System.out::println);
  
//结果:
Student{name='aa', age=100}
Student{name='bb', age=100}

concat 流合并

//创建一个集合,存储多个字符串元素 
ArrayList<String> list = new ArrayList<String>();
list.add("心如音");
list.add("流老蛋");
list.add("王值");
list.add("李尔");
list.add("张新敏");
list.add("张天坤");

//需求 1:取前 4 个数据组成一个流 
Stream<String> s1 = list.stream().limit(4);
//需求 2:跳过 2 个数据组成一个流 
Stream<String> s2 = list.stream().skip(2);
//需求 3:合并需求 1 和需求 2 得到的流,并把结果在控制台输出 
Stream.concat(s1,s2).forEach(System.out::println); 
//需求 4:合并需求 1 和需求 2 得到的流,并把结果在控制台输出,要求字符串元素不能重复 
Stream.concat(s1,s2).distinct().forEach(System.out::println);

终端操作

match 元素匹配

  1. allMatch 匹配所有
List<Integer> integerList = Arrays.asList(12345);
boolean result = integerList.stream().allMatch(i -> i > 3);
if (result) {
    System.out.println("值都大于3");
}
  1. anyMatch 匹配其中一个
List<Integer> integerList = Arrays.asList(12345);
boolean result = integerList.stream().allMatch(i -> i > 3);
if (result) {
    System.out.println("存在大于3的值");
}
  1. noneMatch 全部不匹配
List<Integer> integerList = Arrays.asList(12345);
boolean result = integerList.stream().allMatch(i -> i > 3);
if (result) {
    System.out.println("值都小于3");
}

count 统计元素个数

List<Integer> integerList = Arrays.asList(12345);
Long result = integerList.stream().filter(e -> e > 3).count();

find 查找元素

  1. findFirst:查找第一个
List<Integer> integerList = Arrays.asList(12345);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
  1. findAny:随机查找一个
List<Integer> integerList = Arrays.asList(12345);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();

min、max 获取最值

List<Integer> integerList = Arrays.asList(12345);
Optional<Integer> min = integerList.stream().min(Integer::compareTo);
System.out.println(min.get());
Optional<Integer> max = integerList.stream().max(Integer::compareTo);
System.out.println(max.get());

// 使用 mapToInt
OptionalInt min1 = integerList.stream().mapToInt(e -> e).min();
System.out.println(min1.getAsInt());
OptionalInt max1 = integerList.stream().mapToInt(e -> e).max();
System.out.println(max1.getAsInt());

// 对象使用,例如计算对象 Dish 中的 calories 值
Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);
OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();

reduce 元素组合

把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce

// reduce():字符串拼接
String reduceStr1 = Stream.of("ma""zhi""chu").reduce("", String::concat);
System.out.println(reduceStr1);
String reduceStr2 = Stream.of("ma""zhi""chu").reduce("", (x,y)->x+y);
System.out.println(reduceStr2);

// reduce():求和,identity(起始值)为 0
Integer total1 = Stream.of(1,2,3,4).reduce(0, Integer::sum);
System.out.println(total1);
Integer total2 = Stream.of(1,2,3,4).reduce(0, (x, y) -> x +y);
System.out.println(total2);

// 求和,sumValue = 10, 无起始值
Integer total3 = Stream.of(1234).reduce(Integer::sum).get();
System.out.println(total3);

// reduce():求最小值
double minValue = Stream.of(-1.18.8, -2.2, -6.6).reduce(Double.MAX_VALUE, Double::min);
System.out.println(minValue);

// 对象使用,例如计算对象 Dish 中的 calories 值
Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);

求不同值

  1. summingInt:求和
  2. sum:求和
  3. averagingInt:求平均值
  4. summarizingInt:同时求总和、平均值、最大值、最小值
int sum = menu.stream().collect(Collectors.summingInt(Dish::getCalories));
int sum = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);
int sum = menu.stream().mapToInt(Dish::getCalories).sum();

double average = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));

IntSummaryStatistics intSummaryStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
double average = intSummaryStatistics.getAverage();  //获取平均值
int min = intSummaryStatistics.getMin();  //获取最小值
int max = intSummaryStatistics.getMax();  //获取最大值
long sum = intSummaryStatistics.getSum();  //获取总和

foreach 元素遍历

遍历流中的每一个元素,按照指定的方法执行,执行顺序不一定按照流的顺序

List<Integer> integerList = Arrays.asList(12345);
// foreach: 遍历流中每一个元素, 执行顺序不一定按照流的顺序
integerList.stream().forEach(System.out::println);
// .parallel()表示创建一个并行流
Stream.of(1,2,3,4,5,6).parallel().forEach(System.out::println);

toArray 返回数组

// toArray():将流中的元素放入到一个数组中
String[] strings = Stream.of("ma""zhi""chu").toArray(String[]::new);
System.out.println(Arrays.toString(strings));

collect 返回集合

minBy/maxBy 获取最值

Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(Collectors.minBy(Integer::compareTo));
Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(Collectors.maxBy(Integer::compareTo));

toMap 获取属性映射

List<String> strings = menu.stream().map(Dish::getName).collect(Collectors.toList());
Set<String> sets = menu.stream().map(Dish::getName).collect(Collectors.toSet());

//获取属性和对象本身
Map<Integer, House> houseMap = houses.stream().collect(Collectors.toMap(House::getOwnerid, o -> o));
Map<Integer, House> houseMap1 = houses.stream().collect(Collectors.toMap(House::getOwnerid,  Function.identity()));

//出现重复 id 时,取前面 value 的值,获取取后面放入的 value 值,则覆盖先前的 value 值
houses.stream().collect(Collectors.toMap(House::getOwnerid, House::getHousename,(v1,v2)->v2));
houses.stream().collect(Collectors.toMap(House::getOwnerid, House::getHousename,(v1,v2)->v1));

常用方法:

  1. Collectors.toList()
  2. Collectors.toMap()
  3. Collectors.toSet()
  4. Collectors.toCollection()
  5. Collectors.toConcurrentMap()

counting 统计元素个数

List<Integer> integerList = Arrays.asList(12345);
Long result = integerList.stream().count();
Long result = integerList.stream().collect(Collectors.counting());

joining 拼接流中元素

String result = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));

默认如果不通过 map 方法进行映射处理拼接的 toString 方法返回的字符串,joining 的方法参数为元素的分界符,如果不指定生成的字符串将是一串的

groupingBy 元素分组

// 按 type 字段分组
Map<Type, List<Dish>> result = dishList.stream().collect(Collectors.groupingBy(Dish::getType));

// 按 type 字段分组后,再按 calories 字段分组
Map<String, Map<Integer, List<Dish>>> result = menu.stream().collect(Collectors.groupingBy(Dish::getName,
                Collectors.groupingBy(Dish::getCalories)));
// 源数据
ArrayList<GateScanCodeRecord> objects = new ArrayList<>();
objects.add(new GateScanCodeRecord().setMonth("2020-07").setDay("2020-07-12"));
objects.add(new GateScanCodeRecord().setMonth("2020-06").setDay("2020-06-14"));
objects.add(new GateScanCodeRecord().setMonth("2020-06").setDay("2020-06-12"));
objects.add(new GateScanCodeRecord().setMonth("2020-05").setDay("2020-05-17"));
objects.add(new GateScanCodeRecord().setMonth("2020-05").setDay("2020-05-12"));

// 按月份分组有序
TreeMap<String, List<GateScanCodeRecord>> collect2 = objects.parallelStream().collect(Collectors.groupingBy(GateScanCodeRecord::getMonth, TreeMap::new, Collectors.toList()));

// 自定义分组有序
TreeMap<String, List<GateScanCodeRecord>> collect3 =
            objects.parallelStream().collect(Collectors.groupingBy(GateScanCodeRecord::getMonth,
            () -> new TreeMap<>((o1, o2) -> Math.toIntExact(Long.parseLong(o2.replaceAll("-","")) - Long.parseLong(o1.replaceAll("-","")))),
            Collectors.toList()));

// 两层排序(按月分组排序, 月下的数据按天分组并排序)
TreeMap<String, TreeMap<String, List<GateScanCodeRecord>>> collect = objects.stream()
            .collect(Collectors.groupingBy(GateScanCodeRecord::getMonth,
             () -> new TreeMap<>((o1, o2) -> Math.toIntExact(Long.parseLong(o2.replaceAll("-","")) - Long.parseLong(o1.replaceAll("-","")))),
             Collectors.groupingBy(GateScanCodeRecord::getDay,
             () -> new TreeMap<>((o1, o2) -> Math.toIntExact(Long.parseLong(o2.replaceAll("-","")) - Long.parseLong(o1.replaceAll("-","")))),
             Collectors.toList()))
            );

partitioningBy 元素分区

Map<Boolean, List<Dish>> result = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian));
//等同于
Map<Boolean, List<Dish>> result = menu.stream().collect(Collectors.groupingBy(Dish::isVegetarian));

List<Integer> integerList = Arrays.asList(12345);
Map<Boolean, List<Integer>> result = integerList.stream().collect(Collectors.partitioningBy(i -> i < 3));

groupingBy 和 partitioningBy 区别

  1. partitioningBy:将一组数据分为两组,key 为 ture 和 false 的两组数据(仅能分为两组)
  2. groupingBy:将一组数据按照指定的类型分为 N 组,key 为泛型

mapping 获取属性映射集合

对分组之后的对象集合转换为对象的某个属性的集合

public static void main(String[] args) {
    List<Person> personList = new ArrayList<>();
    // 四个参与测试的小伙伴
    Person tom = new Person("tom""男"11);
    Person amy = new Person("amy""女"13);
    Person ali = new Person("ali""男"12);
    Person daming = new Person("daming""男"13);
    personList.add(tom);
    personList.add(amy);
    personList.add(ali);
    personList.add(daming);
    // 对小伙伴按照性别 age 进行分组
    Map<String, Set<String>> resultMap = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.mapping(Person::getName, Collectors.toSet())));
    System.out.println(resultMap.toString());
}

collectingAndThen 归纳处理

// 按, 拼接成字符串后全部转为大写
String collect = Stream.of("ma""zhi""chu").collect(Collectors.collectingAndThen(Collectors.joining(","),
        String::toUpperCase));
System.out.println(collect);


原文始发于微信公众号(爱编程的小生):Steam流操作

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/314589.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!