一、Stream 核心概念
Stream流可以认为是省事的遍历集合或者其他的包装类,我们可以通过一些方法或者手段获得某个集合的Stream实例对象,然后就可以使用这个对象自带的方法去完成各种各样的操作了;
其实逻辑并不难;
那问题是什么样的对象可以通过手段得到Stream流对象呢?

我们要想使用Stream流的功能,首先就必须先得到Stream流对象,由上诉的表格可以得知,通常情况下,单列集合,数组,和一些类型相同的零散数据可以直接通过各自的手段得到相应的Stream流对象,至于双列集合必须先得到其相应的键值对或者键的Set集合才能得到相应的Stream流对象;
具体的创建流的方式,只要一看代码案例就完全明白了,逻辑比较清晰明了;
二、Stream 操作类型
1. 创建流
主要四类,分别展示相应的创建流的方式
1.1 单列集合
很简单,单列集合有特定的方法直接获得相应的Stream流
List<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("Python");
//直接调用方法获得相应的Stream流,很简单
Stream<String> stream1 = list.stream();
1.2 双列集合
HashMap<String, Integer> hm = new HashMap<>();
hm.put("aaa", 111);
hm.put("bbb", 222);
hm.put("ccc", 333);
hm.put("ddd", 444);
//逻辑依旧清晰明了
Stream<String> stream1 =hm.keySet().stream();
1.3 数组
数组的逻辑稍微有些不同,主要体现在,他要想得到Stream流对象,必须通过数组的静态方法,并不像集合那样有相应的方法直接获取
int[] arr1 = {1,2,3,4,5,6,7,8,9,10};
String[] arr2 = {"a","b","c"};
//其实。逻辑也很清晰,没啥记住就行了
Stream<String> stream1=Arrays.stream(arr1);
Stream<String> stream2=Arrays.stream(arr2);
1.4 同类型的数据
这个也有些不同,直接上代码,一看便知
调用的是Stream自己的方法创建得到的
Stream<String> stream1=Stream.of(1,2,3,4,5);
Stream<String> stream2=Stream.of(“a”, “b”, “c”, “d”, “e”);
2. 流的方法(核心)
创建流肯定是为了使用流的方法;
而Stream流的方法主要分为两大类,通过下面的代码和名字估计就能猜出他们的区别:
具体的请看以下代码案例自行体会;
2.1 中间操作
-
filter()
:会对Stream流中的数据进行过滤和筛选,返回过滤之后的Stream流;map()
:转换元素distinct()
:去重操作,依赖HashCode方法和equals方法limit()
:获取前几个数据skip()
:跳过几个数据后再获取后面全部的数据concat()
:这是Stream类的静态方法,和其他方法有一定区别;合并俩流,最好保证俩流的数据类型是一样的
//准备集合
List<String> words = new ArrayList<>();
words.add("apple");
words.add("banana");
words.add("cherry");
words.add("date");
//Stream流的链式编程成为它的特征和标配
// 过滤操作
words.stream().filter(s -> s.length() > 5).forEach(s->System.out.println(s);
// banana,cherry
// 获取操作
words.stream().limit(2).forEach(s->System.out.println(s); // apple, banana
// 跳获取操作
words.stream().skip(2).forEach(s->System.out.println(s); //cherry, date
//合并
Stream.concat(list1.stream(), list2.stream()).forEach(s -> System.out.println(s));
// 映射操作
words.stream().map(String::length);
2.2. 终止操作
触发实际计算并关闭流,产生最终结果或副作用(如遍历、收集、统计等);最显著的特征是,在链式调用中这几个方法只能作为最后一个方法,因为它们没有返回Stream流;
-
forEach()
:遍历元素 ,返回值为voidcollect()
:收集到集合count()
:统计元素数量reduce()
:聚合计算(如求和)findFirst()
:查找第一个元素toArray()
:将最终结果放大数组之中
//准备集合
List<String> words = new ArrayList<>();
words.add("apple");
words.add("banana");
words.add("cherry");
words.add("date");
// 遍历元素
words.stream().forEach(s -> System.out.println(s));
// 收集结果,转化为什么集合类型取决你想要什么,其集合类型完全和集合原来的特性一样
//诸如:Collectors.toSet(),还有一个Collectors.toMap()用法稍微麻烦些
List<String> filtered = words.stream()
.filter(s -> s.contains("a"))
.collect(Collectors.toList()); // [apple, banana, date]
//如何收集到Map集合之中,关键在于Map集合中键和值分别是什么,其实也不用害怕也挺简单的
//关键就在于.collect(Collectors.toMap(键的规则, 值的规则));中规则如何去写
//还有要注意的就是产生的数据中键不能重复,负责报错,其实也就是让你在生成Map集合时自己掂量掂量
//符不符合生成Map集合最基本的条件
Map<String,String> aFilteredMap = words.stream()
.filter(s -> s.contains("a"))
.collect(Collectors.toMap(
s -> s+"值:",
s -> s
));
// 统计数量
long count = words.stream()
.filter(s -> s.startsWith("a"))
.count(); // 1
//转化数组
String[] arr = words.stream().toArray(value -> new String[value]);
在转化为Map集合时,一定要注意键的唯一性,不然会报错
三、典型应用场景
案例1:对象集合处理
class Person {
String name;
int age;
// 构造方法和getter省略
}
List<Person> people = Arrays.asList(
new Person("Alice", 23),
new Person("Bob", 17),
new Person("Charlie", 35)
);
// 获取成年人姓名列表(年龄>=18)
List<String> adultNames = people.stream()
.filter(p -> p.getAge() >= 18)
.map(Person::getName)
.sorted()
.collect(Collectors.toList());
// 结果:[Alice, Charlie]
通过Stream API可以实现声明式的数据处理,使代码更简洁且易于维护。合理使用能显著提升集合操作效率,特别是在大数据量场景下。