Java Stream APItutorial

MasterJava Stream API, writing high 效, 简洁 流式code

tutorialoverview

本tutorial将详细介绍Java Stream API core concepts and 实践techniques, includingStream creation, in间operation, 终端operation, parallel流etc. in 容. through本tutorial Learning, you willable tousingStream APIwriting更加简洁, high 效 Javacode, 充分利用function式programming 优势.

Stream APIoverview

Stream API is Java 8引入 features, 它providing了一种声明式 方式来processingcollectiondata. Stream API core思想 is 将collectiondata转换 for 流, 然 after through一系列 operation来processing这些data, 最 after 将processing结果收集起来.

Stream 特点

  • 声明式: Stream APIusing声明式 方式processingdata, code更加简洁易读.
  • function式programming: Stream API基于function式programming思想, usingLambda表达式serving asparameter.
  • 链式operation: Stream APIsupport链式operation, 可以将 many 个operation连接 in 一起.
  • parallelprocessing: Stream APIproviding了parallel流, 可以方便地forparallelprocessing.
  • 惰性求值: Stream APIin in间operation is 惰性求值 , 只 has in 终端operation被调用时才会执行.
  • 无副作用: Streamoperation不会modify原始datasources, 而 is 返回一个 new 结果.

Stream creation

in Javain, has many 种方式可以creationStream:

1. from collectioncreationStream

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamCreationExample {
    public static void main(String[] args) {
        List names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        
        //  from collectioncreationStream
        Stream streamFromList = names.stream();
        
        //  from collectioncreationparallelStream
        Stream parallelStreamFromList = names.parallelStream();
        
        // usingStream
        System.out.println("===  from collectioncreationStream ===");
        streamFromList.forEach(System.out::println);
        
        // usingparallelStream
        System.out.println("\n===  from collectioncreationparallelStream ===");
        parallelStreamFromList.forEach(System.out::println);
    }
}

2. from arraycreationStream

import java.util.stream.Stream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.DoubleStream;

public class StreamFromArrayExample {
    public static void main(String[] args) {
        //  from objectarraycreationStream
        String[] names = {"Alice", "Bob", "Charlie"};
        Stream streamFromArray = Stream.of(names);
        
        //  from basicclass型arraycreationStream
        int[] numbers = {1, 2, 3, 4, 5};
        IntStream intStream = IntStream.of(numbers);
        
        // usingStream.ofmethodcreationStream
        Stream streamOf = Stream.of("Alice", "Bob", "Charlie");
        
        // usingStream
        System.out.println("===  from objectarraycreationStream ===");
        streamFromArray.forEach(System.out::println);
        
        System.out.println("\n===  from basicclass型arraycreationStream ===");
        intStream.forEach(System.out::println);
        
        System.out.println("\n=== usingStream.ofmethodcreationStream ===");
        streamOf.forEach(System.out::println);
    }
}

3. creation空Stream

import java.util.stream.Stream;

public class EmptyStreamExample {
    public static void main(String[] args) {
        // creation空Stream
        Stream emptyStream = Stream.empty();
        
        // checkStream is 否 for 空
        System.out.println("空Stream 元素数量: " + emptyStream.count());
    }
}

4. usingStream.builder()creationStream

import java.util.stream.Stream;

public class StreambuilderExample {
    public static void main(String[] args) {
        // usingStream.builder()creationStream
        Stream stream = Stream.builder()
                .add("Alice")
                .add("Bob")
                .add("Charlie")
                .build();
        
        // usingStream
        stream.forEach(System.out::println);
    }
}

5. usingStream.generate()creation无限Stream

import java.util.stream.Stream;

public class StreamGenerateExample {
    public static void main(String[] args) {
        // usingStream.generate()creation无限Stream
        Stream infiniteStream = Stream.generate(Math::random);
        
        // 限制Stream big  small 并using
        System.out.println("=== 生成5个随机数 ===");
        infiniteStream.limit(5).forEach(System.out::println);
    }
}

6. usingStream.iterate()creation无限Stream

import java.util.stream.Stream;

public class StreamIterateExample {
    public static void main(String[] args) {
        // usingStream.iterate()creation无限Stream
        Stream infiniteStream = Stream.iterate(0, n -> n + 2);
        
        // 限制Stream big  small 并using
        System.out.println("=== 生成 before 10个偶数 ===");
        infiniteStream.limit(10).forEach(System.out::println);
    }
}

Stream operationclass型

Stream APIin operation可以分 for 三class:

1. in间operation

in间operation会返回一个 new Stream, 它们 is 惰性求值 , 只 has in 终端operation被调用时才会执行. common in间operationincluding:

  • filter: filter元素
  • map: 转换元素
  • flatMap: 将每个元素转换 for Stream, 然 after 将所 has Streammerge for 一个Stream
  • distinct: 去重
  • sorted: sort
  • peek: 查看元素
  • limit: 限制元素数量
  • skip: 跳过元素

2. 终端operation

终端operation会触发Stream processing, 并返回一个结果 or 副作用. common 终端operationincluding:

  • forEach: 遍历元素
  • collect: 收集元素 to collection
  • count: 计算元素数量
  • sum: 计算元素总 and (仅适用于数值Stream)
  • average: 计算元素平均值 (仅适用于数值Stream)
  • max: find最 big 元素
  • min: find最 small 元素
  • findFirst: find第一个元素
  • findAny: find任意元素
  • anyMatch: check is 否 has 元素匹配条件
  • allMatch: check is 否所 has 元素都匹配条件
  • noneMatch: check is 否没 has 元素匹配条件
  • reduce: 归约operation

3. short 路operation

short 路operation is 指 in 满足一定条件时停止processingStream. common short 路operationincluding:

  • limit: 限制元素数量
  • findFirst: find第一个元素
  • findAny: find任意元素
  • anyMatch: check is 否 has 元素匹配条件
  • allMatch: check is 否所 has 元素都匹配条件
  • noneMatch: check is 否没 has 元素匹配条件

in间operation

1. filteroperation

filteroperation用于filterStreamin 元素, 只保留满足条件 元素.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class FilterExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            numbers.add(i);
        }
        
        // filter出偶数
        List evenNumbers = numbers.stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        
        System.out.println("原始list: " + numbers);
        System.out.println("偶数list: " + evenNumbers);
    }
}

2. mapoperation

mapoperation用于转换Streamin 元素, 将每个元素map to 一个 new 元素.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class MapExample {
    public static void main(String[] args) {
        List names = new ArrayList<>();
        names.add("alice");
        names.add("bob");
        names.add("charlie");
        
        // 将每个名字转换 for  big 写
        List upperCaseNames = names.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());
        
        // 将每个名字转换 for  long 度
        List nameLengths = names.stream()
                .map(String::length)
                .collect(Collectors.toList());
        
        System.out.println("原始list: " + names);
        System.out.println(" big 写list: " + upperCaseNames);
        System.out.println("名字 long 度list: " + nameLengths);
    }
}

3. flatMapoperation

flatMapoperation用于将每个元素转换 for Stream, 然 after 将所 has Streammerge for 一个Stream.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class FlatMapExample {
    public static void main(String[] args) {
        List> lists = new ArrayList<>();
        List list1 = List.of(1, 2, 3);
        List list2 = List.of(4, 5, 6);
        List list3 = List.of(7, 8, 9);
        lists.add(list1);
        lists.add(list2);
        lists.add(list3);
        
        // usingflatMap将嵌套list扁平化 for 单个list
        List flatList = lists.stream()
                .flatMap(List::stream)
                .collect(Collectors.toList());
        
        System.out.println("原始嵌套list: " + lists);
        System.out.println("扁平list: " + flatList);
        
        // 另一个例子: 将string分割 for 字符
        List words = List.of("hello", "world");
        List characters = words.stream()
                .flatMap(word -> word.chars().mapToObj(c -> (char) c))
                .collect(Collectors.toList());
        
        System.out.println("原始单词list: " + words);
        System.out.println("字符list: " + characters);
    }
}

4. sortedoperation

sortedoperation用于 for Streamin 元素forsort.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class SortedExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);
        numbers.add(9);
        
        // 自然sort (升序) 
        List sortedAsc = numbers.stream()
                .sorted()
                .collect(Collectors.toList());
        
        // 自定义sort (降序) 
        List sortedDesc = numbers.stream()
                .sorted((a, b) -> b - a)
                .collect(Collectors.toList());
        
        System.out.println("原始list: " + numbers);
        System.out.println("升序sort: " + sortedAsc);
        System.out.println("降序sort: " + sortedDesc);
        
        //  for stringsort
        List names = List.of("Alice", "Bob", "Charlie", "David");
        List sortedNames = names.stream()
                .sorted()
                .collect(Collectors.toList());
        
        System.out.println("\n原始名字list: " + names);
        System.out.println("sort after 名字list: " + sortedNames);
    }
}

5. distinctoperation

distinctoperation用于去除Streamin 重复元素.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class DistinctExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(2);
        numbers.add(3);
        numbers.add(3);
        numbers.add(3);
        
        // 去重
        List distinctNumbers = numbers.stream()
                .distinct()
                .collect(Collectors.toList());
        
        System.out.println("原始list: " + numbers);
        System.out.println("去重 after list: " + distinctNumbers);
    }
}

6. limit and skipoperation

limitoperation用于限制Streamin 元素数量, skipoperation用于跳过Streamin 元素.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class LimitSkipExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            numbers.add(i);
        }
        
        // 限制元素数量 for 5
        List limitedNumbers = numbers.stream()
                .limit(5)
                .collect(Collectors.toList());
        
        // 跳过 before 5个元素
        List skippedNumbers = numbers.stream()
                .skip(5)
                .collect(Collectors.toList());
        
        // 组合usinglimit and skip, 获取第6 to 第8个元素
        List rangeNumbers = numbers.stream()
                .skip(5)
                .limit(3)
                .collect(Collectors.toList());
        
        System.out.println("原始list: " + numbers);
        System.out.println("限制 after list: " + limitedNumbers);
        System.out.println("跳过 before 5个元素 after list: " + skippedNumbers);
        System.out.println("第6 to 第8个元素: " + rangeNumbers);
    }
}

终端operation

1. forEachoperation

forEachoperation用于遍历Streamin 元素.

import java.util.ArrayList;
import java.util.List;

public class ForEachExample {
    public static void main(String[] args) {
        List names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        
        // usingforEach遍历元素
        System.out.println("=== usingforEach遍历元素 ===");
        names.stream().forEach(System.out::println);
        
        // usingforEachOrdered保持元素顺序 (适用于parallelStream) 
        System.out.println("\n=== usingforEachOrdered遍历元素 ===");
        names.stream().parallel().forEachOrdered(System.out::println);
    }
}

2. collectoperation

collectoperation用于将Streamin 元素收集 to collectionin.

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class CollectExample {
    public static void main(String[] args) {
        List names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("Bob");
        
        // 收集 to List
        List collectedList = names.stream()
                .collect(Collectors.toList());
        
        // 收集 to Set (自动去重) 
        Set collectedSet = names.stream()
                .collect(Collectors.toSet());
        
        // 收集 to specificclass型 collection
        ArrayList collectedArrayList = names.stream()
                .collect(Collectors.toCollection(ArrayList::new));
        
        // 连接string
        String joinedNames = names.stream()
                .collect(Collectors.joining(", "));
        
        System.out.println("原始list: " + names);
        System.out.println("收集 to List: " + collectedList);
        System.out.println("收集 to Set: " + collectedSet);
        System.out.println("收集 to ArrayList: " + collectedArrayList);
        System.out.println("连接string: " + joinedNames);
    }
}

3. countoperation

countoperation用于计算Streamin 元素数量.

import java.util.ArrayList;
import java.util.List;

public class CountExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            numbers.add(i);
        }
        
        // 计算元素数量
        long count = numbers.stream().count();
        
        // 计算偶数数量
        long evenCount = numbers.stream()
                .filter(n -> n % 2 == 0)
                .count();
        
        System.out.println("元素数量: " + count);
        System.out.println("偶数数量: " + evenCount);
    }
}

4. reduceoperation

reduceoperation用于将Streamin 元素归约 for 单个值.

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class ReduceExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            numbers.add(i);
        }
        
        // 计算总 and 
        Optional sumOptional = numbers.stream()
                .reduce((a, b) -> a + b);
        int sum = sumOptional.orElse(0);
        
        // 计算总 and  (带初始值) 
        int sumWithInitial = numbers.stream()
                .reduce(0, (a, b) -> a + b);
        
        // 计算最 big 值
        Optional maxOptional = numbers.stream()
                .reduce((a, b) -> a > b ? a : b);
        int max = maxOptional.orElse(Integer.MIN_VALUE);
        
        // 连接string
        List names = List.of("Alice", "Bob", "Charlie");
        String concatenatedNames = names.stream()
                .reduce("", (a, b) -> a + ", " + b);
        // 去除开头 ", "
        concatenatedNames = concatenatedNames.substring(2);
        
        System.out.println("numberlist: " + numbers);
        System.out.println("总 and : " + sum);
        System.out.println("总 and  (带初始值) : " + sumWithInitial);
        System.out.println("最 big 值: " + max);
        System.out.println("\n名字list: " + names);
        System.out.println("连接 after : " + concatenatedNames);
    }
}

5. findoperation

findFirstoperation用于findStreamin 第一个元素, findAnyoperation用于findStreamin 任意元素.

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class FindExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            numbers.add(i);
        }
        
        // find第一个元素
        Optional first = numbers.stream().findFirst();
        System.out.println("第一个元素: " + first.orElse(-1));
        
        // find第一个偶数
        Optional firstEven = numbers.stream()
                .filter(n -> n % 2 == 0)
                .findFirst();
        System.out.println("第一个偶数: " + firstEven.orElse(-1));
        
        // find任意元素 ( in parallelStreamin更 high 效) 
        Optional any = numbers.stream().parallel().findAny();
        System.out.println("任意元素: " + any.orElse(-1));
    }
}

6. matchoperation

matchoperation用于checkStreamin 元素 is 否满足specific条件.

import java.util.ArrayList;
import java.util.List;

public class MatchExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            numbers.add(i);
        }
        
        // check is 否 has 元素 big 于5
        boolean anyGreaterThan5 = numbers.stream()
                .anyMatch(n -> n > 5);
        System.out.println(" is 否 has 元素 big 于5: " + anyGreaterThan5);
        
        // check is 否所 has 元素都 big 于0
        boolean allGreaterThan0 = numbers.stream()
                .allMatch(n -> n > 0);
        System.out.println(" is 否所 has 元素都 big 于0: " + allGreaterThan0);
        
        // check is 否没 has 元素 big 于10
        boolean noneGreaterThan10 = numbers.stream()
                .noneMatch(n -> n > 10);
        System.out.println(" is 否没 has 元素 big 于10: " + noneGreaterThan10);
    }
}

parallel流

parallel流 is Stream APIproviding 一种parallelprocessingdata 方式, 它可以充分利用 many 核processing器 优势, improvingprocessing big 量data efficiency.

creationparallel流

import java.util.ArrayList;
import java.util.List;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        for (int i = 1; i <= 1000000; i++) {
            numbers.add(i);
        }
        
        // 顺序流
        long startTime = System.currentTimeMillis();
        long sumSequential = numbers.stream()
                .filter(n -> n % 2 == 0)
                .mapToLong(Integer::longValue)
                .sum();
        long endTime = System.currentTimeMillis();
        System.out.println("顺序流processing时间: " + (endTime - startTime) + " 毫秒");
        System.out.println("偶数总 and : " + sumSequential);
        
        // parallel流
        startTime = System.currentTimeMillis();
        long sumParallel = numbers.parallelStream()
                .filter(n -> n % 2 == 0)
                .mapToLong(Integer::longValue)
                .sum();
        endTime = System.currentTimeMillis();
        System.out.println("parallel流processing时间: " + (endTime - startTime) + " 毫秒");
        System.out.println("偶数总 and : " + sumParallel);
        
        // 将顺序流转换 for parallel流
        startTime = System.currentTimeMillis();
        long sumParallelConverted = numbers.stream()
                .parallel() // 转换 for parallel流
                .filter(n -> n % 2 == 0)
                .mapToLong(Integer::longValue)
                .sum();
        endTime = System.currentTimeMillis();
        System.out.println("转换 for parallel流processing时间: " + (endTime - startTime) + " 毫秒");
        System.out.println("偶数总 and : " + sumParallelConverted);
    }
}

parallel流 Notes

  • threadsecurity: parallel流 in processing过程in会using many 个thread, 因此需要确保operation is threadsecurity .
  • 共享status: 应避免 in parallel流inmodify共享status, 否则可能会导致data竞争.
  • performance考量: parallel流并不总 is 比顺序流 fast , for 于 small data集 or 计算密集型operation, 顺序流可能更 fast .
  • sort: parallel流 sortoperation可能比顺序流更 high 效, 因 for 它可以利用parallelsortalgorithms.
  • forEachOrdered: in parallel流inusingforEachOrdered可以保持元素 原始顺序, 但会牺牲一些parallelperformance.

实践case

Stream APIimplementation单词statistics

本caseusingStream APIimplementation一个 simple 单词statisticsfunctions, statistics文本in每个单词 出现次数.

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

public class WordCountExample {
    public static void main(String[] args) {
        String text = "Hello world hello Java Java stream stream API API API";
        
        // 分割文本 for 单词, 转换 for  small 写, filter空string, statistics每个单词 出现次数
        Map wordCount = Arrays.stream(text.split("\\s+"))
                .map(String::toLowerCase)
                .filter(word -> !word.isEmpty())
                .collect(Collectors.groupingBy(word -> word, Collectors.counting()));
        
        // 打印单词statistics结果
        System.out.println("文本: " + text);
        System.out.println("单词statistics结果:");
        wordCount.forEach((word, count) -> {
            System.out.println(word + ": " + count);
        });
        
        // 按出现次数sort
        System.out.println("\n按出现次数sort:");
        wordCount.entrySet().stream()
                .sorted((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()))
                .forEach(entry -> {
                    System.out.println(entry.getKey() + ": " + entry.getValue());
                });
        
        // find出现次数最 many  单词
        System.out.println("\n出现次数最 many  单词:");
        wordCount.entrySet().stream()
                .max((entry1, entry2) -> entry1.getValue().compareTo(entry2.getValue()))
                .ifPresent(entry -> {
                    System.out.println(entry.getKey() + ": " + entry.getValue());
                });
    }
}

互动练习

练习1: StreamBasicsoperation

creation一个List, package含1 to 20 整数. 然 after usingStream API:

  1. filter出能被3整除 number
  2. 将每个number乘以2
  3. sort (降序)
  4. 收集 to 一个 new Listin
  5. 打印结果

练习2: Streamadvancedoperation

creation一个List, package含 many 个string. 然 after usingStream API:

  1. filter出 long 度 big 于3 string
  2. 将每个string转换 for big 写
  3. 去重
  4. 收集 to 一个Setin
  5. 打印结果

练习3: parallel流

creation一个package含1000000个元素 List, 然 after 分别using顺序流 and parallel流计算所 has 元素 总 and , 比较它们 执行时间.

练习4: Stream API and Lambda表达式

creation一个Personclass, package含name and ageproperty. 然 after creation一个List, usingStream API:

  1. filter出年龄 big 于18 人
  2. 按年龄sort
  3. 提取每个人 名字
  4. 收集 to 一个Listin
  5. 打印结果