JAVA8-stream流

Java 8引入的Stream API为处理集合数据提供了一种便利而强大的方式。深入理解Java 8的Stream流包括对Stream的基本概念、操作类型和工作原理的理解。

1. 基本概念:

1.1 Stream
  • Stream是Java 8中处理集合的新抽象,它允许你以声明性的方式处理数据。
  • Stream不是一个数据结构,而是对数据进行操作的一种视图。
  • Stream操作可以是中间操作或终端操作,中间操作返回一个新的Stream,终端操作返回一个最终结果。
1.2 中间操作和终端操作:
  • 中间操作(Intermediate Operations): 这些操作会返回一个新的Stream,允许你在原始Stream的元素上进行一系列的转换和过滤。例如,filtermap等。

  • 终端操作(Terminal Operations): 这些操作会触发Stream的处理,并生成最终的结果。终端操作是Stream流的最后一个操作。例如,forEachcollectreduce等。

2. 操作类型:

2.1 过滤与切片:
  • filter(Predicate<T> predicate):通过谓词(Predicate)过滤元素。
    List<String> filteredList = myList.stream()
                                       .filter(s -> s.startsWith("A"))
                                       .collect(Collectors.toList());
    
2.2 映射:
  • map(Function<T, R> mapper):对每个元素应用一个函数。
    List<Integer> lengthList = myList.stream()
                                    .map(String::length)
                                    .collect(Collectors.toList());
    
2.3 扁平化:
  • flatMap(Function<T, Stream<R>> mapper):将每个元素映射为一个Stream,然后合并这些Stream
    List<String> flatMapList = myList.stream()
                                    .flatMap(s -> Arrays.stream(s.split("")))
                                    .collect(Collectors.toList());
    
2.4 排序:
  • sorted():对元素进行自然排序。
  • sorted(Comparator<T> comparator):使用自定义比较器进行排序。
    List<String> sortedList = myList.stream()
                                   .sorted()
                                   .collect(Collectors.toList());
    
2.5 匹配与查找:
  • anyMatch(Predicate<T> predicate):判断是否至少匹配一个元素。
  • allMatch(Predicate<T> predicate):判断是否全部匹配元素。
  • noneMatch(Predicate<T> predicate):判断是否没有匹配元素。
  • findFirst():返回第一个元素。
  • findAny():返回任意一个元素。
    boolean anyStartsWithA = myList.stream().anyMatch(s -> s.startsWith("A"));
    Optional<String> firstElement = myList.stream().findFirst();
    
2.6 归约:
  • reduce(T identity, BinaryOperator<T> accumulator):从流中的第一个元素开始,进行连续的归约操作。
    Optional<String> concatenatedString = myList.stream()
                                                .reduce((s1, s2) -> s1 + s2);
    

3. 工作原理:

  • 惰性求值: Stream是惰性求值的,只有终端操作被触发时,中间操作才会执行。这样可以避免对整个集合进行操作,提高效率。

  • 并行流: Stream提供了并行处理集合的功能,通过parallel()方法可以将顺序流转为并行流。但在某些情况下,并行流可能不如顺序流效率高,要谨慎使用。

    List<String> parallelList = myList.parallelStream()
                                    .filter(s -> s.startsWith("A"))
                                    .collect(Collectors.toList());
    

4. 收集器(Collector):

  • Collectors类提供了很多静态方法,用于生成各种常用的Collector实现,用于对Stream的元素进行汇总。
  • toList():将元素收集到一个List中。
  • toSet():将元素收集到一个Set中。
  • toMap(keyMapper, valueMapper):将元素收集到一个Map中。
    List<String> list = myList.stream().collect(Collectors.toList());
    Set<String> set = myList.stream().collect(Collectors.toSet());
    Map<String, Integer> map = myList.stream()
                                    .collect(Collectors.toMap(Function.identity(), String::length));
    

5. 分组与分区:

  • groupingBy(classifier):根据分类器对元素进行分组,返回一个Map
  • partitioningBy(predicate):根据谓词进行分区,返回一个Map,其中键是Boolean值。
    Map<Integer, List<String>> groupedByLength = myList.stream()
                                                      .collect(Collectors.groupingBy(String::length));
    
    Map<Boolean, List<String>> partitioned = myList.stream()
                                                  .collect(Collectors.partitioningBy(s -> s.length() > 3));
    

6. 连接字符串:

  • joining():将Stream的元素连接成一个字符串。
    String result = myList.stream().collect(Collectors.joining(", "));
    

7. 自定义收集器:

你也可以创建自定义的收集器,实现Collector接口中的方法,用于灵活地对元素进行汇总。

public class StringCollector implements Collector<String, StringBuilder, String> {

    @Override
    public Supplier<StringBuilder> supplier() {
        return StringBuilder::new;
    }

    @Override
    public BiConsumer<StringBuilder, String> accumulator() {
        return StringBuilder::append;
    }

    @Override
    public BinaryOperator<StringBuilder> combiner() {
        return StringBuilder::append;
    }

    @Override
    public Function<StringBuilder, String> finisher() {
        return StringBuilder::toString;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }
}

使用自定义收集器:

String result = myList.stream().collect(new StringCollector());

8. 并行流的注意事项:

  • 共享变量: 在并行流中,要避免共享可变状态的变量,以免发生竞争条件。
  • 有状态操作: 某些操作是有状态的,如distinct()sorted(),在并行流中可能导致意外结果,要注意使用。
    List<String> parallelList = myList.parallelStream()
                                    .filter(s -> s.startsWith("A"))
                                    .collect(Collectors.toList());
    
  • 数据规模: 并行流在数据规模较大时才能发挥优势,对于小规模数据集,顺序流可能更有效。

  • 操作成本: 有些操作在并行流中的成本可能比在顺序流中高,比如reduce操作。

  • 数据结构: 对于某些数据结构,如ArrayList,并行流的性能较好。但对于LinkedList等非线性的数据结构,可能性能不如顺序流。

10. 常见错误和注意事项:

  • 不可重用: Stream是一次性的,一旦使用完毕就不能再次使用。如果需要多次操作,需要创建新的Stream
    Stream<String> stream = myList.stream();
    stream.filter(s -> s.startsWith("A")).collect(Collectors.toList()); // 正确
    stream.map(String::toUpperCase).collect(Collectors.toList()); // 错误
    
  • 操作顺序: 在进行多个中间操作时,操作的顺序可能影响最终的结果。例如,mapfilter的顺序可能影响性能。
    List<String> result = myList.stream()
                                .map(String::toUpperCase)
                                .filter(s -> s.startsWith("A"))
                                .collect(Collectors.toList());
    
  • 空指针异常: 在使用Stream时,注意空指针异常的可能性。可以使用Optional类型来避免。
    Optional<String> optional = myList.stream().findFirst();
    optional.ifPresent(System.out::println);
    

11. CompletableFuture 异步编程:

CompletableFuture是Java 8引入的用于异步编程的工具类,支持非阻塞式的编程。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, ")
        .thenApplyAsync(s -> s + "World")
        .thenAccept(System.out::println);

future.join(); // 阻塞等待异步任务完成

深入理解Java 8的Stream流涉及到更多的细节和高级用法,上述是其中一部分基础概念和操作类型的介绍。通过实际应用和进一步阅读文档,你可以更全面地理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值