Java里的Stream,越用越强大
By: Date: 2024年7月30日 Categories: 程序 标签:

Stream 是 Java8 引入的一个强大且灵活的API功能,它提供了一种高效、简洁的方式来处理集合数据。Stream 不是一个数据结构,而是一种操作集合(如 List、Set)数据的抽象概念,它可以让你以声明式或流水线的方式对集合中的数据进行处理操作,包括过滤、转换、排序、聚合等。Stream API 提供了丰富的操作类型,使得你可以很方便地对集合数据进行各种复杂的处理。

Stream 的操作分类

Stream 的操作分为两类:中间操作和终止操作。
中间操作:中间操作会返回一个新的 Stream 对象,从而允许你链式调用多个中间操作。中间操作并不会立即执行,而是会延迟执行,直到触发终止操作。中间操作包括:filtermapflatMapdistinctsortedpeeklimitskip 等。
终止操作:终止操作会触发流的遍历和处理,并产生一个最终的结果或副作用。终止操作包括:forEachtoArrayreducecollectminmaxcountallMatchfindAny 等。终止操作之后,流将无法使用,因为它已经完成了所有的操作。

我们可以从多种数据源获取 Stream,包括但不限于集合、数组、静态方法、文件等。

  1. 从集合获取Stream:使用集合的 stream()parallelStream() 方法。
  2. 从数组获取Stream:使用 Arrays.stream(T[] array) 方法。
  3. 使用 Stream 的静态方法:如 Stream.of(Object...)IntStream.range(int startInclusive, int endExclusive)Stream.iterate(T seed, UnaryOperator<T> f)Stream.generate(Supplier<T> s) 等。
  4. 从文件获取Stream:使用 java.nio.file.Files 类的 lines(Path path) 方法来逐行读取文件内容。

使用示例

下面来说说 Stream 到底是怎么使用的。

遍历

Integer[] array = {1, 2, 3, 4, 5};
Arrays.stream(array).forEach(System.out::println);

//数组转为 List 或者 Set
String[] arr= new String[4];
List<String> collect = Stream.of(arr).collect(Collectors.toList());

//字符串拼接
Set<String> resultSet = new HashSet<>();
resultSet.stream().collect(Collectors.joining("","[","]"))

过滤和映射

List<String> mapped = listStr.stream().map(String::toUpperCase).collect(Collectors.toList());

//过滤出符合条件的数据
List<Company> filterList = companyList.stream().filter(a -> a.getCompanyName().equals("主体1")).collect(Collectors.toList());
//从集合中过滤出来符合条件的元素(map只是覆盖属性,filter根据判断属性来collect宿主bean)
//将List转换为Map
List<Company> companyList = new ArrayList<>();
companyList.add(new Company("1000","主体1"));
Map<Integer,Company> companyMap= companyList.stream().collect(Collectors.toMap(Company::getCompanyId,a->a,(k1,k2)-> k1));
//上面使用toMap方法,如果集合对象有重复的key,会报错Duplicate key ....
//当然我们可以用下面的方式来避免这个问题,假设company1,conpany2的id都为1000,用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2
Map<String,String> companyMap= companyList.stream().collect(Collectors.toMap(Company::getCompanyId, Company::getCompanyName,(k1,k2)-> k1,LinkedHashMap::new));

//倘若map的value有null值,则Java8下需要使用Optional处理
//因为Java8下不允许为空,避免二义性。而Java9下已修复该bug,如:
Map<String,String> companyMap=companyList.stream().collect(Collectors.toMap(companyVo -> 
    Optional.ofNullable(companyVo).map(Company::getBizType).orElse(""), 
        companyVo -> Optional.ofNullable(companyVo).map(Company::getCompanyName).orElse(""),
    (key1,key2)->key2));

排序和去重

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
List<Integer> sorted = numbers.stream().sorted().collect(Collectors.toList());

List<Company> distinctList = companyList.stream().distinct() .collect(toList());

//按属性升序排序
columnList = companyList.stream().sorted(Comparator.comparing(Company::getCompanyId)).collect(Collectors.toList());

//先以属性升序,再结果按属性降序
companyList = companyList.stream().sorted(Comparator.comparing(Company::getCompanyId).reversed()).collect(Collectors.toList());

聚合和匹配

Optional<Integer> max = numbers.stream().max((x, y) -> Integer.compare(x, y));

boolean anyGreaterThanFive = numbers.stream().anyMatch(n -> n > 5);

分组

//List里面的对象元素,以某个属性来分组
Set<String> authSet= companyList.stream().map(e -> e.getAuthorityId()).collect(Collectors.toSet());
Map<Integer, List<Company>> groupBy = companyList.stream().collect(Collectors.groupingBy(Company::getProvinceId));

//分组取第一条
companyList.stream().collect(Collectors.groupingBy(Company::getCompanyId, Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))));

规约

//求和: 将集合中的数据按照某个属性求和
List<Inventory> inventoryList =new ArrayList<>();
//符合条件的库存总数(主单位的总数量)
BigDecimal availableQty = inventoryList.stream().map(Inventory::getAvailableQty).reduce(BigDecimal.ZERO, BigDecimal::add);

Stream 的特性和优势

  1. 简洁性:Stream API 使得集合操作更加简洁,易于理解和维护。
  2. 高效性:Stream 操作可以并行执行,提高处理大量数据的效率。
  3. 不可变性:Stream 操作不会改变原始数据源,保证了数据的安全性。
  4. 延迟执行:Stream 操作是延迟执行的,只有在触发终止操作时才会真正执行,这有助于减少不必要的计算和内存使用。

总之通过 Java 8 的 Stream API,你可以以更加灵活和强大的方式处理集合数据,从而编写出更加简洁、高效和可维护的代码。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注