Stream流总结:从入门到精通

Stream流总结:从入门到精通

  • 1. 前言
  • 2. 创建流操作
  • 2. 中间操作
  • 3. 终端操作
  • 4. Stream流线程不安全问题

1. 前言

Stream流是Java 8中引入的一个新特性,它允许以声明性方式处理数据,如过滤、映射、排序等。常用的Stream流方法包括filtermapsortedlimitskipreducecollectforEachfindFirstcount等,这些方法可以根据具体的需求进行组合使用,以实现复杂的流式处理逻辑,但在使用的时候也要注意线程安全问题
Stream流的使用步骤如下:

  • 准备数据源:可以使用集合、数组等作为数据源
  • 获取流对象:通过调用集合、数组等对象的stream()方法来获取流对象
  • 中间操作:使用中间操作来对流中的元素进行过滤(filter)映射(map)排序(sorted)等操作
  • 终结操作:终结操作是用来结束流并收集操作结果的,常用的终结操作包括collect()forEach()

2. 创建流操作

  • 从集合创建
    • 使用stream()方法从集合(如ListSetMap)创建Stream
    • 使用parallelStream()Stream转换为并行流,实现并行处理,提高处理速度
    // 使用stream()方法从集合(如List、Set、Map)创建Stream
    List<String> strings = Arrays.asList("Hello", "World", "Stream1111", "API11111");  
    Stream<String> stringStream = strings.stream();
    
    //使用parallelStream():将Stream转换为并行流,实现并行处理,提高处理速度
    long count = strings.parallelStream()
    .filter(c -> c.length() > 5)
    .count();
    System.out.println(count); // [2]
    
  • 从数组创建
    • 使用Arrays.stream()方法从数组创建流
    • 使用静态方法Stream.of(array)可以获取数组对应的流
    // 使用Arrays.stream()方法从数组创建流
    int[] numbers = {1, 2, 3, 4, 5};
    IntStream stream = Arrays.stream(numbers);
    
    // 使用静态方法Stream.of(array)可以获取数组对应的流
    Stream<String> stream = Stream.of(array); 
    

2. 中间操作

  • filter()

    // filter方法用于过滤出满足特定条件的元素,返回一个包含满足条件的元素的新流
    // 例如:以下代码将过滤出列表中所有的偶数
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);  
    Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0);// [2 4 6 8]
    
  • map()

    // map方法用于将流中的每个元素映射到另一个对象,返回一个由映射结果组成的流
    // 例如:以下代码将字符串列表中的每个字符串转换成小写字母
    List<String> words = Arrays.asList("Hello", "World", "Stream", "API");  
    List<String> lowercaseWords = words.stream()  
        .map(String::toLowerCase)  
        .collect(Collectors.toList());  
    System.out.println(lowercaseWords); // [hello, world, stream, api]
    
  • sorted()

    // sorted方法用于对流中的元素进行排序,返回一个按指定顺序排序的新流
    // 例如:以下代码将列表中的字符串按照字母顺序排序
    List<String> words = Arrays.asList("Hello", "World", "Stream", "API");  
    List<String> sortedWords = words.stream()  
        .sorted()  
        .collect(Collectors.toList());  
    System.out.println(sortedWords); // [API, Hello, Stream, World]
    
  • limit()

    // limit方法用于获取流中的前几个元素,返回一个包含指定数量的元素的新流
    // 例如:以下代码将获取列表中的前三个元素
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);  
    List<Integer> firstThreeNumbers = numbers.stream()  
        .limit(3)  
        .collect(Collectors.toList());  
    System.out.println(firstThreeNumbers); // [1, 2, 3]
    
  • skip()

    // skip方法用于跳过流中的前几个元素,返回一个新的流,该流不包含被跳过的元素
    // 例如:以下代码将跳过列表中的前两个元素
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);  
    List<Integer> skippedNumbers = numbers.stream()  
        .skip(2)  
        .collect(Collectors.toList());  
    System.out.println(skippedNumbers); // [3, 4, 5, 6, 7, 8, 9]
    

3. 终端操作

  • reduce()

    // reduce操作是对Stream中的元素进行归约操作,通常用于计算总和、平均值等聚合数据
    // reduce操作需要一个BinaryOperator作为参数,用于指定如何将两个元素进行归约
    int sum = Arrays.asList(1, 2, 3, 4, 5).stream()  
                     .reduce(0, Integer::sum); // 0是初始值,Integer::sum是归约操作  
    System.out.println(sum); // [15]
    
  • forEach()

    // 使用lambda表达式打印每个字符串到控制台
    List<String> strings = Arrays.asList("Java", "Python", "C++", "JavaScript");  
    List<String> collectedStrings = strings.stream()  
                                           .filter(str -> str.startsWith("J"))  
                                           .forEach(System.out::print); // [Java, JavaScript]
    
  • collect()

    // collect操作用于将Stream中的元素收集到一个集合中
    // 通过collect操作,我们可以将Stream中的元素汇总到List、Set等集合类型中
    List<String> strings = Arrays.asList("Java", "Python", "C++", "JavaScript");  
    List<String> collectedStrings = strings.stream()  
                                           .filter(str -> str.startsWith("J"))  
                                           .collect(Collectors.toList());  
    System.out.println(collectedStrings); // [Java, JavaScript]
    
  • count()

    long count = Arrays.asList(1, 2, 3, 4, 5).stream()  
                        .count(); // 计算Stream中元素的数量  
    System.out.println(count); // [5]
    
  • Collectors.toList()
    // toList()方法将流中的元素收集到一个List中
    List<String> words = Arrays.asList("Java", "Stream", "API");
    List<String> collectedList = words.stream()
    .collect(Collectors.toList());
    // 将流中的元素收集到 List 中
    System.out.println(collectedList); // [Java, Stream, API]
    
  • Collectors.toSet()
    // toSet()方法将流中的元素收集到一个Set中
    List<String> words = Arrays.asList("Java", "Stream", "API");
    Set<String> collectedSet = words.stream()
    .collect(Collectors.toSet());
    // 将流中的元素收集到 Set 中
    System.out.println(collectedSet); // [Java, Stream, API](无序)
    
  • Collectors.toMap()
    // toMap()方法将流中的元素按照键值对的方式收集到一个Map中
    List<String> words = Arrays.asList("Java", "Stream", "API");
    Map<Integer, String> collectedMap = words.stream()
    .collect(Collectors.toMap(String::length, s -> s));
    // 将流中的元素按照长度作为键收集到 Map 中
    System.out.println(collectedMap); // {4=Java, 6=Stream, 3=API}
    
    // 注意: 这种时候一定要进行去重操作,不然会报错,这里如果出现了重复就保留之前的
    allUserList.stream()
                    .collect(Collectors.toMap( CompanyUserListDO::getName,
                            CompanyUserListDO::getCloudUserId,
                            (existing, replacement) -> existing))
    
  • Collectors.joining()
    // joining()方法将流中的元素连接成一个字符串
    List<String> words = Arrays.asList("Java", "Stream", "API");
    String concatenated = words.stream()
    .collect(Collectors.joining(", "));
    // 将流中的元素用逗号连接成字符串
    System.out.println(concatenated); // [Java, Stream, API]
    
  • Collectors.groupingBy()
    // groupingBy()方法按照某个分类器对流中的元素进行分组,生成一个Map
    List<String> words = Arrays.asList("Java", "Stream", "API");
    Map<Integer, List<String>> groupedByLength = words.stream()
    .collect(Collectors.groupingBy(String::length));
    // 按照字符串长度对流中的元素进行分组
    System.out.println(groupedByLength); // {3=[Java], 5=[Stream], 3=[API]}
    
  • Collectors.partitioningBy()
    // partitioningBy()方法根据某个条件对流中的元素进行分区,生成一个Map
    List<String> words = Arrays.asList("Java", "Stream", "API");
    Map<Boolean, List<String>> partitionedByLength = words.stream()
    .collect(Collectors.partitioningBy(s -> s.length() > 4));
    // 根据字符串长度是否大于4对流中的元素进行分区
    System.out.println(partitionedByLength); // 输出:{false=[Java, API], true=[S
    

4. Stream流线程不安全问题

  1. ThreadUnsafeStreamExample_A

    import java.util.Arrays;  
    import java.util.List;  
    import java.util.concurrent.atomic.AtomicInteger;  
      
    public class ThreadUnsafeStreamExample_A {  
        public static void main(String[] args) {  
            List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);  
      
            // 并行流  
            int sum = numbers.parallelStream()  
                              .mapToInt(n -> n) // 映射为整数流  
                              .sum(); // 求和  
      
            System.out.println("Sum: " + sum); // 输出:Sum: 45  
        }  
    }
    

    在上述代码中,我们使用并行流对一个包含9个整数的列表进行求和操作,由于使用了并行流,求和操作会在多个线程上同时进行,如果sum()方法内部存在线程不安全问题,那么最终的结果可能会出现错误。
    为了解决线程不安全问题,可以使用AtomicInteger类来保证线程安全地累加求和的结果。下面是使用AtomicInteger的示例代码:

  2. ThreadSafeStreamExample_B

    import java.util.Arrays;  
    import java.util.List;  
    import java.util.concurrent.atomic.AtomicInteger;  
      
    public class ThreadSafeStreamExample_B {  
        public static void main(String[] args) {  
            List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);  
      
            // 并行流  
            AtomicInteger sum = new AtomicInteger(0);  
            numbers.parallelStream()  
                    .mapToInt(n -> n) // 映射为整数流  
                    .forEach(n -> sum.addAndGet(n)); // 累加求和的结果  
      
            System.out.println("Sum: " + sum.get()); // Sum: 45  
        }  
    }
    

    我们使用AtomicInteger来累加求和的结果,由于AtomicInteger是线程安全的,因此可以在多个线程上同时进行累加操作,而不会出现线程不安全问题,最终的结果将正确地输出为45

你可能感兴趣的:(java基础,java,开发语言,ide,后端)