在 Java 中,Stream 流是 Java 8 引入的一个强大特性,它允许你以声明式的方式处理集合数据。Stream 流提供了一种高效且易于使用的方式来对数据进行过滤、映射、排序、聚合等操作,使代码更加简洁和可读。
1. 获取Stream流
要使用Stream流首先要获取到Stream流
public static void main(String[] args) {
//创建Stream流
//1. 单列集合创建
List<String> list = new ArrayList<>();
Stream<?> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<?> stream2 = set.stream();
Stack<String> stack = new Stack<>();
Stream<?> stream3 = stack.stream();
Queue<String> queue = new LinkedList<>();
Stream<?> stream4 = stack.stream();
//2. 双列集合创建
Map<String, String> map = new HashMap<>();
Stream<?> stream5 = map.entrySet().stream();
//3. 数组创建
int[] arr = {1, 2 ,3};
IntStream stream6 = Arrays.stream(arr);
//4. 零散数据创建
Stream<?> stream7 = Stream.of(1, 2, 3, 4);
}
注意 Stream.of(T... values) values为变长参数可以接收一个数组,但是这里只能接收引用类型的数组,如果是基础类型的数据由于不会自动装包会把整个数组视为一个数据
2. 中间方法
当我们获取到Stream流后就可以调用其中的一些方法来对其中的数据进行一些操作,其中的方法分为两大类,其一就是中间方法,中间方法的特点是会返回一个新的 Stream 流,允许你链式调用多个操作 :
- Stream<T> filter(Predicate<T>):过滤满足条件的元素。
- Stream<T> limit(long):限制元素数量。
- Stream<T> skip(long):跳过前 n 个元素。
- Stream<T> distinct():去重。
- static<T> Stream<T> concat(Stream,Stream ):合并a和两个流作为一个流
- Stream<T> sorted():排序。
- Stream<T> map(Function<T, R>):将元素转换为另一种类型。
注意:
- 每个Stream流只能使用一次,中间方法返回的是一个新的Stream流
- 修改Stream流中的数据不会影响原来集合或数组中的数据
2.1 filter使用示例
filter方法接收一个Predicate类型的参数,我们转到其定义:
可以看到这是一个函数式接口, 同时test方法的描述也说明了,如果参数匹配则返回true否则返回false,这里的含义即为,如果该数据(t)是符合要求的则通过,否则不通过:
public static void filterTest() {
List<String> list = new ArrayList<>();
Collections.addAll(list, "张明", "张三丰", "张良", "张明朱诺", "杨过", "小龙女");
//这里筛选以张开头且长度为3的字符串
list.stream()
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(x-> System.out.print(x + " "));
}
这里我们使用lambad表达式重写test方法,先赛选了以张开头的数据,再赛选了长度为3的数据最后使用forEach输出
2.2 limit 和skip使用
limit接收一个long类型的参数表示只保留前n个元素,skip同样接收一个long类型的参数表示要跳过前n个元素:
public static void limitAndSkipTest() {
List<String> list = new ArrayList<>();
Collections.addAll(list, "张明", "张三丰", "张良", "张明朱诺", "杨过", "小龙女");
System.out.println("limit:");
list.stream()
.limit(4)
.forEach(x-> System.out.print(x + " "));
System.out.println("\nskip:");
list.stream()
.skip(3)
.forEach(x-> System.out.print(x + " "));
System.out.println("\nlimit-skip:");
list.stream()
.limit(4)
.skip(3)
.forEach(x-> System.out.print(x + " "));
System.out.println("\nskip-limit:");
list.stream()
.skip(3)
.limit(4)
.forEach(x-> System.out.print(x + " "));
}
运行结果:
2.3 distinct 和concat
public static void distinctAndConcatTest() {
List<String> list1 = new ArrayList<>();
Collections.addAll(list1, "张三丰", "张三丰", "杨过", "张明朱诺", "杨过", "小龙女");
List<String> list2 = new ArrayList<>();
Collections.addAll(list2, "张明", "张三丰", "迪迦");
System.out.println("\ndistinct:");
list1.stream()
.distinct()
.forEach(x-> System.out.print(x + " "));
System.out.println("\nconcat:");
Stream.concat(list1.stream(), list2.stream())
.forEach(x-> System.out.print(x + " "));
}
运行结果:
2.4 stored使用
public static void sortedTest() {
List<String> list1 = new ArrayList<>();
Collections.addAll(list1, "张明", "张三丰", "迪迦");
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 1, 2, 3);
System.out.println("\nsortedList1:");
list1.stream()
.sorted()
.forEach(x-> System.out.print(x + " "));
System.out.println("\nsortedList2:");
list2.stream()
.sorted()
.forEach(x-> System.out.print(x + " "));
System.out.println("\nsortedList2desc:");
list2.stream()
.sorted(((o1, o2) -> o2 - o1))
.forEach(x-> System.out.print(x + " "));
}
sorted不传参数时默认是排降序,可以传入比较器自定义排序规则
2.5 map使用
public static void MapTest() {
List<String> list1 = new ArrayList<>();
Collections.addAll(list1, "张明-16", "张三丰-23", "迪迦-3000");
//获取每个人的年龄
/**
* 第一个参数对应原本的数据类型,第二个参数代表要转成的数据类型
*/
list1.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[1]);
}
}).forEach(x-> System.out.print(x + " "));
System.out.println();
//使用lambada表达式
list1.stream()
.map(s -> Integer.parseInt(s.split("-")[1]))
.forEach(x-> System.out.print(x + " "));
}
运行结果:
3. 终结方法
可以理解为结束方法,不会再返回Stream流
常用的终结方法有:
- forEach(Consumer<T>):遍历每个元素。
- count():计算元素数量。
- toArray():将元素转换为数组。
- collect(Collector<T, A, R>):将元素收集到集合中。
3.1 forEach/count/toArray()使用
public static void CountAndToArrayTest() {
List<String> list1 = new ArrayList<>();
Collections.addAll(list1, "张三丰", "张三丰", "杨过", "张明朱诺", "杨过", "小龙女");
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 1, 2, 3);
System.out.println(list1.stream().count());
System.out.println(list2.stream().count());
//<>为需要创建的数组的类型
String[] str = list1.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
//value代表数据的数量,我们返回一个长度大于等于value的数据即可
return new String[value];
}
});
System.out.println(Arrays.toString(str));
//使用lambada表达式
Integer[] arr = list2.stream().toArray(value -> new Integer[value]);
System.out.println(Arrays.toString(arr));
}
3.2 collect 使用
将流中的元素收集到集合中
public static void CollectTest() {
List<String> list1 = new ArrayList<>();
Collections.addAll(list1, "张三丰-男-23", "杨过-男-32", "张三丰-男-23", "张明朱诺-女-19", "小龙女-女-18");
//收集所有男性
//List
List<String> list = list1.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());
System.out.println(list);
//Set
Set<String> set = list1.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());
System.out.println(set);
//Map
//收集男性,名字作为键,年龄作为值
Map<String, Integer> map1 = list1.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.distinct()//map中key不能重复,所以先去重
.collect(Collectors.toMap(
new Function<String, String>() {
@Override
public String apply(String s) {
return s.split("-")[0];
}
},
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[2]);
}
}
));
System.out.println(map1);
//使用lambada表达式
Map<String, Integer> map2 = list1.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.distinct()//map中key不能重复,所以先去重
.collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[2])
));
System.out.println(map2);
}
这里特别说明toMap()方法,
可以看到toMap接收两个Function做为参数,这里通过参数名我们不难猜出,一个作为键,一个作为值,于是我们只需实现Function接口重写其中方法使其返回预期的键/值即可。