目录
1.void forEach(Consumer action); 遍历流的元素
2.Optional max(比较器); //获取流中元素的最大值,但是必须传入比较器
1.Stream filter( Predicate pre );
2.Stream map(Function mapper);
3.Stream sorted(比较器); // 传入比较器排序
4.static Stream concat(Stream s1 , Stream s2);
Stream 流是什么
在Java中,
Stream
是 Java 8 引入的一个重要特性,它是对集合数据进行操作的一种新的方式。使用Stream
可以让你写出更加简洁、高效的代码。它支持各种包括过滤、映射、汇总等在内的操作,可以链式调用,使得数据处理变得更加灵活。
Stream 流的作用
- 数据处理:提供了一种高效并且灵活的方式来处理数据。
- 延迟执行:某些操作(如
filter
)不会立即执行,直到遇到终止操作(如collect
或forEach
)才会执行。- 并行操作:可以很容易地创建并行流来利用多核处理器的性能。
如何获取 Stream 对象
获取
Stream
对象的主要方式有以下几种:
- 从 Collection 获取:任何实现了
Iterable
接口的对象都可以通过stream()
方法来创建一个串行流,或者通过parallelStream()
方法创建一个并行流。- 数组转 Stream:可以通过
Arrays.stream(array)
方法将数组转换成 Stream。- 创建无限流:可以使用
Stream.iterate()
或者Stream.generate()
创建无限流。- 从特定类的方法中获取:例如
Files.lines(path)
可以从文件中读取每一行生成一个 Stream。
下面是一些基本的 Stream
操作示例:
终结方法:
方法调用完毕后返回的不在是一个流的对象, 不能继续在使用Stream的功能
1.void forEach(Consumer<? super T> action); 遍历流的元素
list.stream().forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } });
2.Optional max(比较器); //获取流中元素的最大值,但是必须传入比较器
String s1 = list.stream().max((o1, o2) -> o2.length() - o1.length()).get();
3.long count(); 获取流中元素的个数
long count = list.stream().count();
中间方法:
方法调用完毕后返回的依然是一个流的对象, 可以继续使用Stream的功能
1.Stream<T> filter( Predicate<T> pre );
假设有一个
List<String>
并且你想保留其中长度大于3的字符串:import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = Arrays.asList("Tom", "Jerry", "Tuffy", "Spike"); // 使用 filter 方法过滤长度大于3的字符串 names.stream() .filter(name -> name.length() > 3) .forEach(System.out::println); } }
2.Stream<T> map(Function<? super T, ? extends R> mapper);
假设你想将上述列表中的每个元素转换为大写:
// 继续上面的例子 names.stream() .map(String::toUpperCase) .forEach(System.out::println);
3.Stream<T> sorted(比较器); // 传入比较器排序
如果你想要根据名字的长度来排序:
// 继续上面的例子 names.stream() .sorted(Comparator.comparingInt(String::length)) .forEach(System.out::println);
4.static Stream<T> concat(Stream<T> s1 , Stream<T> s2);
ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "张三丰丰", "张无忌", "周芷若", "赵敏", "张强"); Stream.concat( list.stream().filter(s -> s.startsWith("张")),list.stream().filter(s -> s.length() == 3) ) .forEach(s-> System.out.println(s));
Stream流的注意事项
当使用 Java 中的
Stream
API 时,有一些需要注意的事项可以帮助你更好地理解和使用它们:
惰性求值:大部分中间操作(如
filter
,map
,flatMap
等)都是惰性的,它们并不会立即执行,只有在触发终端操作(如forEach
,collect
,reduce
等)时,中间操作才会被执行。管道化:一旦定义了一个流,就可以在一个表达式中链接多个操作,形成一个处理管道。这使得代码更易于阅读和维护。
不可变性:流本身是不可变的,一旦创建就不能改变其状态。所有的操作都会返回新的流。
一次性:除非明确指定(如通过
peek
),否则每个流只能被“消费”一次。一旦执行了终端操作,流就无法再次被使用。并行流:虽然并行流可以提高执行效率,但它并不是所有场景下的最佳选择。并行流消耗更多的系统资源,并且对于涉及大量同步或阻塞操作的情况可能没有帮助。
短路行为:一些终端操作,比如
findFirst
,anyMatch
,noneMatch
在满足条件时会提前终止,这种短路行为可以节省不必要的计算。数据源类型:创建流时要考虑到数据源的类型。对于有限的数据源(如集合),可以直接创建流;而对于无限的数据源(如迭代器产生的数据),需要谨慎处理避免无限循环。
性能考虑:尽管
Stream
提供了更简洁的语法,但在某些情况下,传统的循环可能更快。尤其是当你处理大数据集时,要评估性能影响。异常处理:流的操作可能会抛出异常,特别是在使用自定义函数作为参数时。需要确保正确处理这些异常。
线程安全:并行流在多线程环境中运行,因此需要考虑线程安全问题,尤其是在使用共享资源时。