Java8 Stream 使用详解
- 一、流的定义
- 二、流的创建
- 三、常用流
- 1. 中间操作
- 2. 终端操作
- 2.1. forEach(Consumer<T> action):
- 2.2. collect(Collector<T, A, R> collector):
- 2.3. count():
- 2.4. anyMatch(Predicate<T> predicate):
- 2.5. allMatch(Predicate<T> predicate):
- 2.6. noneMatch(Predicate<T> predicate):
- 2.7. reduce(BinaryOperator<T> accumulator):
- 2.8. min(Comparator<T> comparator):
- 2.9. max(Comparator<T> comparator):

一、流的定义
- Java 8引入了Stream流的概念,它是对集合对象(Collection)进行操作的高级抽象。
- Stream流的操作分为两种:中间操作和终端操作。
- 中间操作是指在流上进行的操作,返回的仍然是一个流,可以进行链式操作,而终端操作是指对流的最终操作,返回的是一个结果或者一个副作用。
- Stream流的特点如下:
- Stream流是对集合对象的一种封装,使得可以用函数式编程的方式对集合进行操作。
- Stream流的操作是延迟执行的,只有在终端操作时才会触发执行。
- Stream流可以进行并行操作,提高了程序的性能。
二、流的创建
1. 通过集合创建流:
可以通过集合类的stream()方法或parallelStream()方法来创建一个流。例如:
List<String> list = Arrays.asList(“a”, “b”, “c”);
Stream<String> stream = list.stream();
2. 通过数组创建流:
可以通过Arrays类的stream()方法来创建一个数组流。例如:
String[] array = {“a”, “b”, “c”};
Stream<String> stream = Arrays.stream(array);
3. 通过Stream.of()创建流:
可以使用Stream类的of()方法来创建一个流。例如:
Stream<String> stream = Stream.of(“a”, “b”, “c”);
4. 通过Stream.generate()创建流:
可以使用Stream类的generate()方法来创建一个无限流,需要提供一个Supplier来生成流中的元素。例如:
Stream<String> stream = Stream.generate(() -> “element”);
5. 通过Stream.iterate()创建流:
可以使用Stream类的iterate()方法来创建一个无限流,需要提供一个初始值和一个UnaryOperator来生成流中的元素。例如:
Stream<Integer> stream = Stream.iterate(0, n -> n + 1);
6. 通过文件、网络、IO流等方式来创建流:
例如:
Stream<String> stream = Files.lines(Paths.get(“file.txt”));
Stream<String> stream = BufferedReader.lines();
这些是Java 8中常用的几种创建流Stream的方式,可以根据具体的需求选择适合的方式来创建流。
三、常用流
1. 中间操作
1.1. filter(Predicate predicate):
根据给定的条件对流进行过滤,只保留满足条件的元素。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamFilterDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用filter()方法过滤出偶数
Stream<Integer> evenNumbersStream = numbers.stream()
.filter(number -> number % 2 == 0);
// 打印过滤后的结果
evenNumbersStream.forEach(System.out::println);
}
}
在上面的示例中,我们首先创建了一个包含整数的列表numbers。然后,我们使用stream()方法将列表转换为流,然后使用filter()方法对流进行过滤,只保留满足条件(即为偶数)的元素。最后,我们使用forEach()方法打印过滤后的结果。
注意:filter()方法返回一个新流,而不会修改原始流。因此,在使用filter()方法后,如果想要继续对流进行其他操作,需要将结果保存到一个新的流中。
1.2. map(Function<T, R> mapper):
将流中的每个元素通过给定的映射函数进行转换。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamMapDemo {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 将名字转换为大写
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 打印转换后的结果
upperCaseNames.forEach(System.out::println);
}
}
在上面的示例中,我们首先创建了一个包含字符串的列表names。然后,我们使用stream()方法将列表转换为流,然后使用map()方法对流中的每个元素进行转换,将名字转换为大写。最后,我们使用collect()方法将转换后的结果收集到一个新的列表中,并使用forEach()方法打印结果。
在map()方法中,我们传递了一个方法引用String::toUpperCase,它代表了一个从String类型到String类型的映射函数,将字符串转换为大写字母。map()方法会将流中的每个元素都应用这个映射函数,并返回一个新的流,其中包含了转换后的结果。
注意:map()方法返回一个新流,而不会修改原始流。因此,在使用map()方法后,如果想要继续对流进行其他操作,需要将结果保存到一个新的流中。
1.3. flatMap(Function<T, Stream> mapper):
将流中的每个元素通过给定的映射函数转换为一个新的流,并将所有流合并为一个流。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFlatMapDemo {
public static void main(String[] args) {
List<List<Integer>> numbers = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
// 将二维列表转换为一维列表
List<Integer> flattenedList = numbers.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 打印转换后的结果
flattenedList.forEach(System.out::println);
}
}
在上面的示例中,我们首先创建了一个包含多个列表的二维列表numbers。然后,我们使用stream()方法将二维列表转换为流,然后使用flatMap()方法对流中的每个元素进行转换,将每个列表转换为一个流,并将所有流合并为一个流。最后,我们使用collect()方法将转换后的结果收集到一个新的列表中,并使用forEach()方法打印结果。
在flatMap()方法中,我们传递了一个方法引用List::stream,它代表了一个从List类型到Stream类型的映射函数,将列表转换为流。flatMap()方法会将流中的每个元素都应用这个映射函数,并返回一个新的流,其中包含了转换后的结果。最后,flatMap()方法会将所有的流合并为一个流。
注意:flatMap()方法返回一个新流,而不会修改原始流。因此,在使用flatMap()方法后,如果想要继续对流进行其他操作,需要将结果保存到一个新的流中。
1.4. sorted():
对流进行排序,默认按照元素的自然顺序进行排序。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamSortedDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 3, 8, 2, 9, 1);
// 对流进行排序
List<Integer> sortedList = numbers.stream()
.sorted()
.collect(Collectors.toList());
// 打印排序后的结果
sortedList.forEach(System.out::println);
}
}
在上面的示例中,我们首先创建了一个整数列表numbers。然后,我们使用stream()方法将列表转换为流,并使用sorted()方法对流中的元素进行排序。由于没有指定排序规则,所以默认按照元素的自然顺序进行排序。最后,我们使用collect()方法将排序后的结果收集到一个新的列表中,并使用forEach()方法打印结果。
注意:sorted()方法返回一个新流,而不会修改原始流。因此,在使用sorted()方法后,如果想要继续对流进行其他操作,需要将结果保存到一个新的流中。
1.5. distinct():
去除流中的重复元素。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDistinctDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 4, 3, 5);
// 去除流中的重复元素
List<Integer> distinctList = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 打印去重后的结果
distinctList.forEach(System.out::println);
}
}
在上面的示例中,我们首先创建了一个整数列表numbers,其中包含一些重复的元素。然后,我们使用stream()方法将列表转换为流,并使用distinct()方法去除流中的重复元素。最后,我们使用collect()方法将去重后的结果收集到一个新的列表中,并使用forEach()方法打印结果。
注意:distinct()方法会根据元素的hashCode()和equals()方法来判断元素是否重复。因此,如果要去除流中的自定义对象的重复元素,需要正确实现该对象的hashCode()和equals()方法。
1.6. limit(long maxSize):
限制流的大小,只取前N个元素。
import java.util.stream.Stream;
public class StreamLimitDemo {
public static void main(String[] args) {
// 创建一个包含整数的无限流
Stream<Integer> infiniteStream = Stream.iterate(0, i -> i + 1);
// 限制流的大小,只取前5个元素
Stream<Integer> limitedStream = infiniteStream.limit(5);
// 打印限制后的结果
limitedStream.forEach(System.out::println);
}
}
在上面的示例中,我们首先创建了一个包含整数的无限流,使用Stream.iterate()方法从0开始生成整数。然后,我们使用limit()方法限制流的大小,只取前5个元素。最后,我们使用forEach()方法打印限制后的结果。
limit()方法接受一个参数maxSize,表示要限制的大小。它会返回一个新的流,其中包含原始流中的前maxSize个元素。如果原始流的大小小于maxSize,则返回原始流的所有元素。
使用limit()方法可以在处理大量数据时,只取需要的部分数据进行操作,提高程序的性能。
2. 终端操作
2.1. forEach(Consumer action):
对流中的每个元素执行给定的操作。
import java.util.stream.Stream;
public class StreamForEachDemo {
public static void main(String[] args) {
// 创建一个包含字符串的流
Stream<String> stringStream = Stream.of("apple", "banana", "cherry");
// 对流中的每个元素执行操作
stringStream.forEach(s -> System.out.println("Fruit: " + s));
}
}
在上面的示例中,我们首先创建了一个包含字符串的流,使用Stream.of()方法创建一个包含"apple"、"banana"和"cherry"的流。然后,我们使用forEach()方法对流中的每个元素执行操作,即打印出"Fruit: "加上元素的值。
forEach()方法接受一个Consumer接口的实现,用于定义对每个元素的操作。在示例中,我们使用了Lambda表达式来实现Consumer接口,打印出每个水果的名字。
使用forEach()方法可以对流中的每个元素执行任意操作,如打印、计算、过滤等。它是一种简洁、方便的遍历流元素的方式。
2.2. collect(Collector<T, A, R> collector):
将流中的元素收集到一个集合中,可以是List、Set、Map等。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamCollectDemo {
public static void main(String[] args) {
// 创建一个包含整数的流
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
// 将流中的元素收集到List集合中
List<Integer> numberList = numberStream.collect(Collectors.toList());
// 打印收集到的List集合
System.out.println(numberList);
}
}
在上面的示例中,我们首先创建了一个包含整数的流,使用Stream.of()方法创建一个包含1、2、3、4、5的流。然后,我们使用collect()方法将流中的元素收集到List集合中,通过传递Collectors.toList()作为参数,表示将元素收集到一个List集合中。
使用collect()方法可以将流中的元素收集到各种集合中,例如List、Set、Map等。Collectors类提供了许多静态方法,用于创建各种收集器,以满足不同的需求。
你可以根据需要选择适合的收集器,然后将其传递给collect()方法,将流中的元素收集到相应的集合中。
2.3. count():
返回流中的元素个数。
import java.util.stream.Stream;
public class StreamCountDemo {
public static void main(String[] args) {
// 创建一个包含整数的流
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
// 获取流中的元素个数
long count = numberStream.count();
// 打印元素个数
System.out.println("Number of elements: " + count);
}
}
在上面的示例中,我们首先创建了一个包含整数的流,使用Stream.of()方法创建一个包含1、2、3、4、5的流。然后,我们使用count()方法获取流中的元素个数,将结果保存在一个long类型的变量中。
最后,我们打印出元素个数。注意,count()方法返回的是一个long类型的值,表示流中的元素个数。
使用count()方法可以方便地获取流中元素的个数,无需遍历整个流。这对于需要统计元素个数的场景非常有用。
2.4. anyMatch(Predicate predicate):
检查流中是否存在满足给定条件的元素。
import java.util.stream.Stream;
public class StreamAnyMatchDemo {
public static void main(String[] args) {
// 创建一个包含字符串的流
Stream<String> stringStream = Stream.of("apple", "banana", "orange", "grape");
// 检查流中是否存在长度大于5的元素
boolean anyMatch = stringStream.anyMatch(s -> s.length() > 5);
// 打印结果
if (anyMatch) {
System.out.println("存在长度大于5的元素");
} else {
System.out.println("不存在长度大于5的元素");
}
}
}
在上面的示例中,我们首先创建了一个包含字符串的流,使用Stream.of()方法创建一个包含"apple"、“banana”、“orange”、"grape"的流。然后,我们使用anyMatch()方法检查流中是否存在长度大于5的元素,通过Lambda表达式s -> s.length() > 5来定义判断条件。
最后,我们根据anyMatch()方法的返回结果打印出相应的结果。
使用anyMatch()方法可以方便地检查流中是否存在满足给定条件的元素,无需遍历整个流。这对于需要判断某个条件是否成立的场景非常有用。
2.5. allMatch(Predicate predicate):
检查流中的所有元素是否都满足给定条件。
import java.util.stream.Stream;
public class StreamAllMatchDemo {
public static void main(String[] args) {
// 创建一个包含整数的流
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
// 检查流中是否所有元素都大于0
boolean allMatch = numberStream.allMatch(n -> n > 0);
// 打印结果
if (allMatch) {
System.out.println("所有元素都大于0");
} else {
System.out.println("不是所有元素都大于0");
}
}
}
在上面的示例中,我们首先创建了一个包含整数的流,使用Stream.of()方法创建一个包含1、2、3、4、5的流。然后,我们使用allMatch()方法检查流中是否所有元素都大于0,通过Lambda表达式n -> n > 0来定义判断条件。
最后,我们根据allMatch()方法的返回结果打印出相应的结果。
使用allMatch()方法可以方便地检查流中的所有元素是否都满足给定条件,如果流中的元素个数很大,一旦发现有某个元素不满足条件,就会立即返回false,无需遍历整个流。这对于需要判断所有元素是否满足某个条件的场景非常有用。
2.6. noneMatch(Predicate predicate):
检查流中的所有元素是否都不满足给定条件。
import java.util.stream.Stream;
public class StreamNoneMatchDemo {
public static void main(String[] args) {
// 创建一个包含整数的流
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
// 检查流中是否所有元素都不等于0
boolean noneMatch = numberStream.noneMatch(n -> n == 0);
// 打印结果
if (noneMatch) {
System.out.println("所有元素都不等于0");
} else {
System.out.println("有元素等于0");
}
}
}
在上面的示例中,我们首先创建了一个包含整数的流,使用Stream.of()方法创建一个包含1、2、3、4、5的流。然后,我们使用noneMatch()方法检查流中是否所有元素都不等于0,通过Lambda表达式n -> n == 0来定义判断条件。
最后,我们根据noneMatch()方法的返回结果打印出相应的结果。
使用noneMatch()方法可以方便地检查流中的所有元素是否都不满足给定条件,如果流中的元素个数很大,一旦发现有某个元素满足条件,就会立即返回false,无需遍历整个流。这对于需要判断所有元素是否都不满足某个条件的场景非常有用。
2.7. reduce(BinaryOperator accumulator):
将流中的元素进行归约操作,返回一个Optional对象。
import java.util.Optional;
import java.util.stream.Stream;
public class StreamReduceDemo {
public static void main(String[] args) {
// 创建一个包含整数的流
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
// 对流中的元素进行求和
Optional<Integer> sum = numberStream.reduce((a, b) -> a + b);
// 打印结果
sum.ifPresent(result -> System.out.println("求和结果为: " + result));
}
}
在上面的示例中,我们首先创建了一个包含整数的流,使用Stream.of()方法创建一个包含1、2、3、4、5的流。然后,我们使用reduce()方法将流中的元素进行求和操作,通过Lambda表达式(a, b) -> a + b来定义求和的规则。
reduce()方法返回一个Optional对象,表示归约操作的结果。我们可以使用ifPresent()方法检查Optional对象是否有值,如果有值则打印出结果。
使用reduce()方法可以对流中的元素进行各种归约操作,如求和、求最大值、求最小值等。根据传入的归约规则,可以对元素进行累积操作,得到一个最终的结果。
2.8. min(Comparator comparator):
返回流中的最小元素。
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamMinDemo {
public static void main(String[] args) {
// 创建一个包含字符串的流
Stream<String> stringStream = Stream.of("apple", "banana", "orange", "grape");
// 找到字典序最小的字符串
Optional<String> minString = stringStream.min(Comparator.naturalOrder());
// 打印结果
minString.ifPresent(result -> System.out.println("最小的字符串为: " + result));
}
}
在上面的示例中,我们首先创建了一个包含字符串的流,使用Stream.of()方法创建一个包含"apple"、“banana”、“orange”、"grape"的流。然后,我们使用min()方法找到字典序最小的字符串,通过Comparator.naturalOrder()方法指定字符串的比较规则。
min()方法返回一个Optional对象,表示流中的最小元素。我们可以使用ifPresent()方法检查Optional对象是否有值,如果有值则打印出结果。
使用min()方法可以根据指定的比较器找到流中的最小元素。根据比较规则,可以比较不同类型的元素,如整数、字符串等。
2.9. max(Comparator comparator):
返回流中的最大元素。
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamMaxDemo {
public static void main(String[] args) {
// 创建一个包含整数的流
Stream<Integer> integerStream = Stream.of(5, 2, 8, 1, 10);
// 找到最大的整数
Optional<Integer> maxInteger = integerStream.max(Comparator.naturalOrder());
// 打印结果
maxInteger.ifPresent(result -> System.out.println("最大的整数为: " + result));
}
}
在上面的示例中,我们首先创建了一个包含整数的流,使用Stream.of()方法创建一个包含5、2、8、1、10的流。然后,我们使用max()方法找到最大的整数,通过Comparator.naturalOrder()方法指定整数的比较规则。
max()方法返回一个Optional对象,表示流中的最大元素。我们可以使用ifPresent()方法检查Optional对象是否有值,如果有值则打印出结果。
使用max()方法可以根据指定的比较器找到流中的最大元素。根据比较规则,可以比较不同类型的元素,如整数、字符串等。