目录
常见的Stream中间方法(Intermediate Operations)包括:
常见的Stream终结方法(Terminal Operations)包括:
什么是stream流
在Java中,Stream流是Java 8引入的一种新的抽象概念,它允许对集合(Collection)对象进行函数式风格的操作。Stream可以让你以一种声明式的方式处理数据集合,而不用关心底层的实现细节。
Stream流的特点包括:
-
惰性求值(Lazy Evaluation):Stream操作会延迟执行,只有当需要结果时才会触发执行。这种惰性求值的特性可以提高性能,因为只有在需要时才会进行计算。
-
支持链式操作(Chainable Operations):可以将多个操作连接起来形成一个操作链,从而简化代码,提高可读性。
-
内部迭代(Internal Iteration):与传统的集合迭代方式相比,Stream流采用内部迭代的方式,可以更加灵活地对数据进行处理。
-
不可变性(Immutability):Stream操作不会修改原始数据,而是返回一个新的Stream,这样可以确保数据的不可变性,减少了并发环境下的问题。
Stream流的优点:
- 简化集合操作:Stream提供了丰富的API,可以轻松地进行过滤、映射、排序等操作,大大简化了集合操作的代码。
- 更具表现力:函数式风格的Stream操作使得代码更具表现力和可读性。
- 并行处理支持:Stream提供了并行处理的支持,可以充分利用多核处理器的优势,提高程序的性能。
Stream流的缺点:
- 学习曲线较陡:对于初学者来说,学习Stream的概念可能需要一定的时间和精力。
- 性能开销:在一些简单的情况下,使用Stream可能会引入一些额外的性能开销,尤其是在数据量较小的情况下。
- 不易调试:由于Stream操作是惰性求值的,当出现问题时,可能比较难以调试。
Stream流的注意事项
-
避免使用无限流:使用
Stream.generate()
和Stream.iterate()
等方法创建的流可能是无限的,需要谨慎使用以避免出现内存溢出等问题。 -
考虑并行流的适用性:虽然并行流可以提高处理速度,但并不是所有情况下都适用。对于简单的操作或者数据量较小的情况,使用并行流可能会带来额外的性能开销。因此,需要根据具体情况权衡是否使用并行流。
-
避免副作用:尽量避免在Stream操作中引入副作用(如修改外部变量、进行I/O操作等),这会使代码难以理解和调试,并可能引发并发安全问题。
-
处理异常:在对流进行操作时,可能会出现一些异常情况,需要在合适的地方进行异常处理。
-
考虑性能:虽然Stream提供了便利的操作方式,但在一些性能要求较高的场景下,可能需要考虑使用传统的迭代方式来替代Stream操作,以提高性能。
-
理解操作的顺序:Stream操作是按照声明的顺序依次执行的,需要确保操作的顺序符合预期。
-
不可重用:Stream流是单次使用的,一旦对流进行操作,就不能再次使用同一个流对象。如果需要多次操作相同的数据集合,需要创建新的流对象。
-
合理使用中间操作和终端操作:Stream操作分为中间操作和终端操作两种类型,中间操作用于构建操作链,而终端操作会触发实际的计算。合理使用这两种操作可以提高代码的可读性和性能。
如何获取stream流
从集合获取:
可以通过集合的stream()
方法获取Stream流。
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
从数组获取:
可以使用Arrays.stream()
方法将数组转换为Stream流。
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
从文件获取:
可以使用Files.lines()
方法读取文件内容并返回一个包含文件行的Stream流。
try (Stream<String> stream = Files.lines(Paths.get("filename.txt"))) {
// 在这里使用文件流进行操作
} catch (IOException e) {
e.printStackTrace();
}
常见的Stream中间方法(Intermediate Operations)包括:
-
filter(Predicate):根据指定的条件过滤流中的元素。
-
map(Function):将流中的每个元素映射为另一个元素。
-
sorted() / sorted(Comparator):对流中的元素进行排序,可以指定自定义的比较器。
-
distinct():去除流中重复的元素。
-
limit(long):截断流,使其最多只包含指定数量的元素。
-
skip(long):跳过指定数量的元素,返回剩余的元素流。
-
flatMap(Function):将流中的每个元素转换为一个流,然后将这些流合并为一个流。
-
peek(Consumer):对流中的每个元素执行操作,同时保留流的原始元素。
常见的Stream终结方法(Terminal Operations)包括:
-
forEach(Consumer):对流中的每个元素执行指定的操作。
-
collect(Collector):将流中的元素收集到一个集合中。
-
count():返回流中的元素数量。
-
anyMatch(Predicate) / allMatch(Predicate) / noneMatch(Predicate):检查流中是否存在任意、所有或者没有元素满足指定条件。
-
findFirst() / findAny():返回流中的第一个或者任意一个元素。
-
reduce():对流中的元素进行归约操作,返回一个结果。
-
min(Comparator) / max(Comparator):返回流中的最小或者最大元素。
-
toArray():将流中的元素转换为数组。
代码实例
从集合获取Stream流,并对自定义类进行操作:
这个示例演示了如何从一个包含自定义类(Person)对象的集合中获取Stream流,并对这些对象进行操作。具体来说:
1.自定义了一个 Person 类,其中包含姓名(name)和年龄(age)属性。
2.创建了一个包含几个 Person 对象的列表。
3.使用 stream() 方法从列表中获取一个 Stream 流。
4. 使用中间方法 filter() 过滤出年龄大于30的人。
5.使用中间方法 map() 将 Person 对象映射为姓名字符串。
6.使用终结方法 collect() 将结果收集到一个新的列表中。
7.打印结果,显示年龄大于30的人的姓名列表。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class StreamExample1 {
public static void main(String[] args) {
// 从集合获取Stream流
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
);
System.out.println("Original list: " + people);
// 使用中间方法和终结方法
List<String> namesOver30 = people.stream()
.filter(person -> person.getAge() > 30) // 过滤年龄大于30的人
.map(Person::getName) // 获取姓名
.collect(Collectors.toList()); // 收集结果到List中
// 打印结果
System.out.println("Names of people over 30: " + namesOver30);
}
}
从数组获取Stream流,并对自定义类进行操作:
这个示例演示了如何从一个包含自定义类(Point)对象的数组中获取 Stream 流,并对这些对象进行操作。具体来说:
1.自定义了一个 Point 类,表示二维平面中的一个点,包含 x 和 y 坐标。
2.创建了一个包含几个 Point 对象的数组。
3.使用 Stream.of() 方法从数组中获取一个 Stream 流。
4.使用中间方法 filter() 过滤出 x 坐标大于2的点。
5.使用中间方法 map() 对符合条件的点进行坐标翻倍。
6.使用终结方法 forEach() 对结果进行遍历,并打印每个点的坐标。
import java.util.stream.Stream;
class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
}
public class StreamExample2 {
public static void main(String[] args) {
// 从数组获取Stream流
Point[] points = {
new Point(1, 2),
new Point(3, 4),
new Point(5, 6)
};
// 使用中间方法和终结方法
Stream.of(points)
.filter(point -> point.getX() > 2) // 过滤x大于2的点
.map(point -> new Point(point.getX() * 2, point.getY() * 2)) // 坐标翻倍
.forEach(System.out::println); // 打印结果
}
}