Stream
1.创建流
1.1 集合创建流
List<String> list = List.of("a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
1.2 数组创建流
String[] array = {"a","b","c"};
Stream<String> stream = Arrays.stream(array);
stream.forEach(System.out::println);
1.3 Stream.of()
Stream<String> stream = Stream.of("a", "b", "c");
stream.forEach(System.out::println);
合并流:
Stream<String> stream1 = Stream.of("a", "b", "c");
Stream<String> stream2 = Stream.of("e", "f", "g");
Stream<String> concat = Stream.concat(stream1, stream2);
concat.forEach(System.out::println);
1.4 创建一个能动态加入元素的流
Stream.Builder<String> stringBuilder = Stream.builder();
stringBuilder.add("a");
stringBuilder.add("b");
if (Math.random() > 0.5) {
stringBuilder.add("c");
}
Stream<String> stream = stringBuilder.build();
stream.forEach(System.out::println);
注: build() 以后就不能再往stream中添加元素, 否则会报IllegalStateException
1.5 从文件创建流
Path path = Paths.get("file.txt");
try (Stream<String> lines = Files.lines(path)){
lines.forEach(System.out::println);
} catch (IOException e) {
throw new RuntimeException(e);
}
每行文本是一个元素
1.6 创建基本类型的流
以IntStream为例
- IntStream.of
IntStream intStream = IntStream.of(1, 2, 3);
intStream.forEach(System.out::println);
- 创建指定范围的IntStream
IntStream intStream = IntStream.range(1,4);
intStream.forEach(System.out::println);// 1 2 3
IntStream intStream = IntStream.rangeClosed(1,4);
intStream.forEach(System.out::println);// 1 2 3 4
- 创建n个包含随机数的流
IntStream intStream = IntStream.rangeClosed(1,4);
new Random().ints(5)
.forEach(System.out::println);
// 1555978888
// 1191117249
// -1175853188
// 249139444
// -1230944054
- 基本类型的流转为对象流
IntStream intStream = IntStream.rangeClosed(1,4);
Stream<Integer> integerStream = intStream.boxed();
1.7 创建无限流
generate:
- 生个n个相同元素的流
Stream<String> stream = Stream.generate(() -> "kiddkid").limit(4);
// 防止无限流生成, 限制个数
stream.forEach(System.out::println);
// kiddkid
// kiddkid
// kiddkid
// kiddkid
- 生成含n个随机数的流
Stream.generate(Math::random).limit(5).forEach(System.out::println);
// 0.9578029464950379
// 0.03290878021143662
// 0.7683317134743166
// 0.7188884787961349
// 0.8739307746551834
iterate
生成数学序列或实现迭代算法
// // (起始元素, 生成条件)
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
stream.forEach(System.out::println);
0
2
4
6
8
// (起始元素, 结束条件, 生成条件)
Stream<Integer> stream = Stream.iterate(0,n -> n <= 10, n -> n + 2);
stream.forEach(System.out::println);
1.8 创建并行流
- 在已有流的基础上创建并行流
Stream<Integer> stream = Stream.iterate(0,n -> n <= 10, n -> n + 2);
Stream<Integer> parallel = stream.parallel();
- 集合直接创建并行流
List.of("a","b","c").parallelStream()
.forEach(System.out::println);// b c a
2.流的中间操作
2.1 筛选和切片
- 根据条件筛选
List<Person> people = List.of(
new Person("Neo",45, "USA"),
new Person("Stan",10,"USA"),
new Person("Grace",16, "UK"),
new Person("Alex",20,"UK"),
new Person("Sebastian",40,"FR")
);
people.stream()
.filter(person -> person.getAge() > 18)
.forEach(System.out::println);
Person{name=‘Neo’, age=45, country=‘USA’}
Person{name=‘Alex’, age=20, country=‘UK’}
Person{name=‘Sebastian’, age=40, country=‘FR’}
- 元素去重
Stream.of("apple","orange","apple","orange","cherry")
.distinct()
.forEach(System.out::println);
apple
orange
cherry
对自定义类要重写equals 和 hashCode:
List<Person> people = List.of(
new Person("Neo",45, "USA"),
new Person("Stan",10,"USA"),
new Person("Grace",16, "UK"),
new Person("Alex",20,"UK"),
new Person("Alex",20,"UK"),
new Person("Sebastian",40,"FR"),
new Person("Sebastian",40,"FR")
);
people.stream()
.distinct()
.forEach(System.out::println);
Person{name=‘Neo’, age=45, country=‘USA’}
Person{name=‘Stan’, age=10, country=‘USA’}
Person{name=‘Grace’, age=16, country=‘UK’}
Person{name=‘Alex’, age=20, country=‘UK’}
Person{name=‘Sebastian’, age=40, country=‘FR’}
- limit 截取流中指定个数的元素
List<Person> people = List.of(
new Person("Neo",45, "USA"),
new Person("Stan",10,"USA"),
new Person("Grace",16, "UK"),
new Person("Alex",20,"UK"),
new Person("Sebastian",40,"FR")
);
people.stream()
.limit(2)
.forEach(System.out::println);
Person{name=‘Neo’, age=45, country=‘USA’}
Person{name=‘Stan’, age=10, country=‘USA’}
注: 若指定元素个数大于整个流的长度, 将会返回整个流
- skip 省略流中指定个数的元素
List<Person> people = List.of(
new Person("Neo",45, "USA"),
new Person("Stan",10,"USA"),
new Person("Grace",16, "UK"),
new Person("Alex",20,"UK"),
new Person("Sebastian",40,"FR")
);
people.stream()
.skip(2)
.forEach(System.out::println);
Person{name=‘Grace’, age=16, country=‘UK’}
Person{name=‘Alex’, age=20, country=‘UK’}
Person{name=‘Sebastian’, age=40, country=‘FR’}
- map 改变流中元素的类型
List<Person> people = List.of(
new Person("Neo",45, "USA"),
new Person("Stan",10,"USA"),
new Person("Grace",16, "UK"),
new Person("Alex",20,"UK"),
new Person("Sebastian",40,"FR")
);
Stream<Person> stream = people.stream();
Stream<String> nameStream = stream.map(person -> person.getName());
nameStream.forEach(System.out::println);
Neo
Stan
Grace
Alex
Sebastian
2.2 映射
- flatMap实现单层流
List<List<Person>> peopleGroup = List.of(
List.of(
new Person("Neo",45, "USA"),
new Person("Stan",10,"USA")
),
List.of(
new Person("Grace",16, "UK"),
new Person("Alex",20,"UK")
),
List.of(
new Person("Sebastian",40,"FR")
)
);
Stream<List<Person>> peopleGroupStream = peopleGroup.stream();
peopleGroupStream.forEach(System.out::println);
[Person{name=‘Neo’, age=45, country=‘USA’}, Person{name=‘Stan’, age=10, country=‘USA’}]
[Person{name=‘Grace’, age=16, country=‘UK’}, Person{name=‘Alex’, age=20, country=‘UK’}]
[Person{name=‘Sebastian’, age=40, country=‘FR’}]
修改:
peopleGroupStream.flatMap(people -> people.stream()).forEach(System.out::println);
Person{name=‘Neo’, age=45, country=‘USA’}
Person{name=‘Stan’, age=10, country=‘USA’}
Person{name=‘Grace’, age=16, country=‘UK’}
Person{name=‘Alex’, age=20, country=‘UK’}
Person{name=‘Sebastian’, age=40, country=‘FR’}
注: 流的操作应该是链式操作, 不能对同一个流进行多次操作
Stream<List<Person>> peopleGroupStream = peopleGroup.stream();
Stream<Person> personStream = peopleGroupStream.flatMap(Collection::stream);
peopleGroupStream.limit(1);
链式操作如下:
peopleGroup.stream()
.flatMap(Collection::stream)
.map(Person::getName)
.forEach(System.out::println);
// or 两者实现效果相同
peopleGroup.stream()
.flatMap(people -> people.stream().map(Person::getName))
.forEach(System.out::println);
Neo
Stan
Grace
Alex
Sebastian
flatMap可以实现任何map的操作, 同时将流转为单层流
- 将流映射为指定类型的流
List<Person> people = List.of(
new Person("Neo", 45, "USA"),
new Person("Stan", 10, "USA"),
new Person("Grace", 16, "UK"),
new Person("Alex", 20, "UK"),
new Person("Sebastian", 40, "FR")
);
IntStream intStream = people.stream().mapToInt(Person::getAge);
intStream.forEach(System.out::println);
45
10
16
20
40
2.3 排序
排序分为自然排序和自定义排序。
- 当流中的元素类型实现了Comparable接口的时候,可以直接调用无参数的sorted(), 按照自然顺序进行排序。
Stream.of("blueberry","cherry","apple","pear")
.sorted()
.forEach(System.out::println);
apple
blueberry
cherry
pear
- 可以定义一个比较器去自定义比较规则。
Stream.of("blueberry","cherry","apple","pear")
.sorted(Comparator.comparingInt(String::length))
.forEach(System.out::println);
pear
apple
cherry
blueberry
Stream.of("blueberry","cherry","apple","pear")
.sorted(Comparator.comparingInt(String::length).reversed())
.forEach(System.out::println);
blueberry
cherry
apple
pear
2.4 综合实践
List<List<Person>> peopleGroup = List.of(
List.of(
new Person("Neo",45, "USA"),
new Person("Stan",10,"USA")
),
List.of(
new Person("Grace",16, "UK"),
new Person("Alex",20,"UK"),
new Person("Alex",20,"UK")
),
List.of(
new Person("Sebastian",40,"FR"),
new Person("Sebastian",40,"FR")
)
);
peopleGroup.stream()
.flatMap(people -> people.stream())
.filter(person -> person.getAge() > 18)
.distinct()
.sorted(Comparator.comparingInt(Person::getAge).reversed())
.map(Person::getName)
.limit(3)
.skip(1)
.forEach(System.out::println);
Sebastian
Alex
我们通常用变量来保存中间操作的结果,以便在需要的时候触发执行.
3.终端操作
Stream 的终结操作通常会返回单个值,一旦一个 Stream 实例上的终结操作被调用,流内部元素的迭代以及流处理调用链上的中间操作就会开始执行,当迭代结束后,终结操作的返回值将作为整个流处理的返回值被返回。
anyMatch
anyMatch() 方法以一个 Predicate (java.util.function.Predicate 接口,它代表一个接收单个参数并返回参数是否匹配的函数)作为参数,启动 Stream 的内部迭代,并将 Predicate 参数应用于每个元素。如果 Predicate 对任何元素返回了 true(表示满足匹配),则 anyMatch() 方法的结果返回 true。如果没有元素匹配 Predicate,anyMatch() 将返回 false。
public class StreamAnyMatchExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
boolean anyMatch = stream.anyMatch((value) -> value.startsWith("One"));
System.out.println(anyMatch);
}
}
上面例程的运行结果是 true , 因为流中第一个元素就是以 “One” 开头的,满足 anyMatch 设置的条件。
allMatch
allMatch() 方法同样以一个 Predicate 作为参数,启动 Stream 中元素的内部迭代,并将 Predicate 参数应用于每个元素。如果 Predicate 为 Stream 中的所有元素都返回 true,则 allMatch() 的返回结果为 true。如果不是所有元素都与 Predicate 匹配,则 allMatch() 方法返回 false。
public class StreamAllMatchExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
boolean allMatch = stream.allMatch((value) -> value.startsWith("One"));
System.out.println(allMatch);
}
}
上面的例程我们把流上用的 anyMatch 换成了 allMatch ,结果可想而知会返回 false,因为并不是所有元素都是以 “One” 开头的。
noneMatch
Match 系列里还有一个 noneMatch 方法,顾名思义,如果流中的所有元素都与作为 noneMatch 方法参数的 Predicate 不匹配,则方法会返回 true,否则返回 false。
public class StreamNoneExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("abc");
stringList.add("def");
Stream<String> stream = stringList.stream();
boolean noneMatch = stream.noneMatch((element) -> {
return "xyz".equals(element);
});
System.out.println("noneMatch = " + noneMatch); //输出 noneMatch = true
}
}
collect
collect() 方法被调用后,会启动元素的内部迭代,并将流中的元素收集到集合或对象中。
public class StreamCollectExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
List<String> stringsAsUppercaseList = stream
.map(value -> value.toUpperCase())
.collect(Collectors.toList());
System.out.println(stringsAsUppercaseList);
}
}
collect() 方法将收集器 – Collector (java.util.stream.Collector) 作为参数。在上面的示例中,使用的是 Collectors.toList() 返回的 Collector 实现。这个收集器把流中的所有元素收集到一个 List 中去。
count
count() 方法调用后,会启动 Stream 中元素的迭代,并对元素进行计数。
public class StreamExamples {
public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
long count = stream.flatMap((value) -> {
String[] split = value.split(" ");
return Arrays.asList(split).stream();
}).count();
System.out.println("count = " + count); // count = 14
}
}
上面的例程中,首先创建一个字符串 List ,然后获取该 List 的 Stream,为其添加了 flatMap() 和 count() 操作。 count() 方法调用后,流处理将开始迭代 Stream 中的元素,处理过程中字符串元素在 flatMap() 操作中被拆分为单词、合并成一个由单词组成的 Stream,然后在 count() 中进行计数。所以最终打印出的结果是 count = 14。
findAny
findAny() 方法可以从 Stream 中找到单个元素。找到的元素可以来自 Stream 中的任何位置。且它不提供从流中的哪个位置获取元素的保证。
public class StreamFindAnyExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("one");
Stream<String> stream = stringList.stream();
Optional<String> anyElement = stream.findAny();
if (anyElement.isPresent()) {
System.out.println(anyElement.get());
} else {
System.out.println("not found");
}
}
}
findAny() 方法会返回一个 Optional,意味着 Stream 可能为空,因此没有返回任何元素。我们可以通过 Optional 的 isPresent() 方法检查是否找到了元素。
Optional 类是一个可以为 null 的容器对象。如果值存在则 isPresent() 方法会返回true,调用get()方法会返回容器中的对象,否则抛出异常:NoSuchElementException
findFirst
findFirst() 方法将查找 Stream 中的第一个元素,跟 findAny() 方法一样,也是返回一个 Optional,我们可以通过 Optional 的 isPresent() 方法检查是否找到了元素。
public class StreamFindFirstExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("one");
Stream<String> stream = stringList.stream();
Optional<String> anyElement = stream.findFirst();
if (anyElement.isPresent()) {
System.out.println(anyElement.get());
} else {
System.out.println("not found");
}
}
}
forEach
forEach() 方法我们在介绍 Collection 的迭代时介绍过,当时主要是拿它来迭代 List 的元素。它会启动 Stream 中元素的内部迭代,并将 Consumer (java.util.function.Consumer, 一个函数式接口,上面介绍过) 应用于 Stream 中的每个元素。 注意 forEach() 方法的返回值是 void。
public class StreamExamples {
public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();
stringList.add("one");
stringList.add("two");
stringList.add("three");
Stream<String> stream = stringList.stream();
stream.forEach(System.out::println);
}
}
注意,上面例程中 forEach 的参数我们直接用了Lambda 表达式引用方法的简写形式。
min
min() 方法返回 Stream 中的最小元素。哪个元素最小是由传递给 min() 方法的 Comparator 接口实现来确定的。
public class StreamMinExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("abc");
stringList.add("def");
Stream<String> stream = stringList.stream();
// 作为 min 方法参数的Lambda 表达式可以简写成 String::compareTo
// Optional<String> min = stream.min(String::compareTo);
Optional<String> min = stream.min((val1, val2) -> {
return val1.compareTo(val2);
});
String minString = min.get();
System.out.println(minString); // abc
}
}
min() 方法返回的是一个 Optional ,也就是它可能不包含结果。如果为空,直接调用 Optional 的 get() 方法将抛出 异常–NoSuchElementException。比如我们把上面的 List 添加元素的两行代码注释掉后,运行程序就会报
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at com.example.StreamMinExample.main(StreamMinExample.java:21)
所以最好先用 Optional 的 ifPresent() 判断一下是否包含结果,再调用 get() 获取结果。
max
与 min() 方法相对应,max() 方法会返回 Stream 中的最大元素,max() 方法的参数和返回值跟 min() 方法的也都一样,这里就不再过多阐述了,只需要把上面求最小值的方法替换成求最大值的方法 max() 即可。
Optional<String> min = stream.max(String::compareTo);
reduce
reduce() 方法,是 Stream 的一个聚合方法,它可以把一个 Stream 的所有元素按照聚合函数聚合成一个结果。reduce()方法接收一个函数式接口 BinaryOperator 的实现,它定义的一个apply()方法,负责把上次累加的结果和本次的元素进行运算,并返回累加的结果。
public class StreamReduceExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
Optional<String> reduced = stream.reduce((value, combinedValue) -> combinedValue + " + " + value);
// 写程序的时候记得别忘了 reduced.ifPresent() 检查结果里是否有值
System.out.println(reduced.get());
}
}
reduce() 方法的返回值同样是一个 Optional 类的对象,所以在获取值前别忘了使用 ifPresent() 进行检查。
stream 实现了多个版本的reduce() 方法,还有可以直接返回元素类型的版本,比如使用 reduce 实现整型Stream的元素的求和
public class IntegerStreamReduceSum {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
intList.add(10);
intList.add(9);
intList.add(8);
intList.add(7);
Integer sum = intList.stream().reduce(0, Integer::sum);
System.out.printf("List 求和,总和为%s\n", sum);
}
}
toArray
toArray() 方法是一个流的终结操作,它会启动流中元素的内部迭代,并返回一个包含所有元素的 Object 数组。
List<String> stringList = new ArrayList<String>();
stringList.add("One flew over the cuckoo's nest");
stringList.add("To kill a muckingbird");
stringList.add("Gone with the wind");
Stream<String> stream = stringList.stream();
Object[] objects = stream.toArray();
不过 toArray 还有一个重载方法,允许传入指定类型数组的构造方法,比如我们用 toArray 把流中的元素收集到字符串数组中,可以这么写:
String[] strArray = stream.toArray(String[]::new);