Java中的Stream API 是Java 8中引入的一个关键抽象概念,它允许你以声明方式处理数据集合(包括数组、集合等)。Stream API提供了一种高效且易于使用的方式来处理数据序列,包括过滤、映射、排序、归约等操作。使用Stream API可以使代码更加简洁、易于理解和维护。
一、Stream的特点
1. 非破坏性:Stream操作不会修改原始数据源。每次操作都会返回一个新的Stream实例。
2. 懒加载:Stream操作是延迟执行的,只有在需要结果时才执行。
3. 可组合性:多个Stream操作可以连接在一起形成一个查询/表达式。
4. 函数式编程:Stream API大量使用了Lambda表达式和函数式编程的概念。
二、Stream的基本操作
2.1获取Stream流
获取方式如下:
获取方式 | 方法名 | 说明 |
单列集合 | Default Stream<E>stream() | Collection中的默认方法 |
双列集合 | 无 | (可以转换成单列集合) |
数组 | Public static<T>Stream<T>stream(T[]array) | Arrays工具类中的静态方法 |
零散数据 | Public static<T>Stream<T>of(T…values) | Stream接口中的静态方法 |
1、单列集合
package com.stream;
import java.util.ArrayList;
import java.util.Collections;
public class StreamDemo1 {
public static void main(String[] args) {
//单列集合获取stream流
ArrayList<String> list=new ArrayList<>();
Collections.addAll(list,"b","c","d");
list.stream().forEach(s -> System.out.println(s));
}
}
2、双列集合
package com.stream;
import java.util.HashMap;
public class StreamDemo2 {
public static void main(String[] args) {
//双列集合获取stream流
HashMap<String,Integer> hm=new HashMap<>();
hm.put("aaa",111);
hm.put("bbb",222);
hm.put("ccc",333);
hm.put("ddd",444);
//第一种获取stream流
//hm.keySet().stream().forEach(s -> System.out.println(s));
//第二种获取stream流
hm.entrySet().stream().forEach(s->System.out.println(s));
}
}
3、数组
package com.stream;
import java.util.Arrays;
public class StreamDemo3 {
//数组获取stream流
public static void main(String[] args) {
int[]arr={1,2,3,4,5,6,7,8,9};
Arrays.stream(arr).forEach(s-> System.out.println(s));
}
}
4、零散数据
package com.stream;
import java.util.stream.Stream;
public class StreamDemo4 {
public static void main(String[] args) {
//一堆零散的数据获取
Stream.of(1,2,3,4,5).forEach(s-> System.out.println(s));
Stream.of("a","b","c","d").forEach(s-> System.out.println(s));
}
}
2.2 Stream流中间方法
以下是一些常见的Java Stream中间操作:
方法名 | 作用 |
filter(Predicate<? super T> predicate) | 过滤流中的元素,只保留那些满足给定谓词的元素。 |
limit(long maxSize) | 获取前几个元素 |
skip(long n) | 跳过流中的前n个元素 |
distinct() | 元素去重。(通过元素的hashCode()和equals()方法判断) |
concat | 把流合并在一起 |
map | 转换流中的数据类型 |
package com.stream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class StreamDemo11 {
//stream流的中间方法
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
Collections.addAll(list,"张三","张三","张三","张四","张四","王五","张明","小明","小张");
//1、过滤--filter,张开头的留下
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
System.out.println("---------------------------------------------");
//2、获取前几个元素--limit
list.stream().limit(4).forEach(s -> System.out.println(s));
System.out.println("---------------------------------------------");
//3、跳过前几个元素--skip
list.stream().skip(3).forEach(s -> System.out.println(s));
System.out.println("---------------------------------------------");
//4、元素去重--distinct
list.stream().distinct().forEach(s -> System.out.println(s));
System.out.println("---------------------------------------------");
//5、合并两个流为一个流--concat
List<String> list1=new ArrayList<>();
Collections.addAll(list1,"aaa","bbb","ccc","ddd");
Stream.concat(list.stream(),list1.stream()).forEach(s -> System.out.println(s));
System.out.println("---------------------------------------------");
//6、转换流中的数据类型--map
List<String> list2=new ArrayList<>();
Collections.addAll(list2,"张三-23","李四-24","王五-25","赵六-26");
list2.stream().map(s -> Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.println(s));
}
}
2.3 Stream流终结方法
终端操作:产生一个结果或副作用,标记着Stream管道的结束。常用方法如下:
方法名 | 作用 |
forEach() | 遍历 |
count() | 统计 |
toArray() | 收集流中的数据放到数组中 |
Collect(Collector collector) | 收集流中的数据放到集合中 |
package com.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class StreamDemo22 {
//stream流的终结方法
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
Collections.addAll(list,"张三","张三","张三","张四","张四","王五","张明","小明","小张");
//1、遍历--forEach
list.stream().forEach(s -> System.out.println(s));
System.out.println("---------------------------------------");
//2、统计--count
long count = list.stream().count();
System.out.println(count);
System.out.println("---------------------------------------");
//3、收集流中的数据放到数组中--toArray()
Object[] arr1= list.stream().toArray();
System.out.println(Arrays.toString(arr1));//数组转字符串
System.out.println("---------------------------------------");
// toArray() 收集流中的数据,放到数组中
// 0bject[] arr1 = list.stream().toArray();//System.out.println(Arrays.toString(arr1));
//IntFunction的泛型:具体类型的数组
//apply的形参:流中数据的个数,要跟数组的长度保持一致//apply的返回值:具体类型的数组//方法体:就是创建数组
//toArray方法的参数的作用:负责创建一个指定类型的数组
//toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组当中//toArray方法的返回值:是一个装着流里面所有数据的数组
/* String[] arr = list.stream().toArray(new IntFunction<String[]>(){
@Override
public string[] apply(int value) {
return new String[value];
});
System.out.println(Arrays.toString(arr));*/
String[] arr2 = list.stream().toArray(value -> new String[value]);
System.out.println(Arrays.toString(arr2));
}
}
package com.stream;
import java.util.*;
import java.util.stream.Collectors;
public class StreamDemo222 {
//stream流的终结方法--collect
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
Collections.addAll(list,"张三-男-23","李四四-女-14","李四四-女-14","李四-男-24","王无无-女-15","王五-男-25","张三三-女-13");
//1、把男的收集到list集合中
List<String> newList = list.stream().
filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());
System.out.println(newList);
//2、把女的收集到set集合中
Set<String> newSet = list.stream().filter(s -> "女".equals(s.split("-")[1]))
.collect(Collectors.toSet());
System.out.println(newSet);//set去重
//3、把女的收集到Map集合中
//键:姓名 值:年龄
Map<String, Integer> newMap = list.stream().distinct()//这里用中间方法distinct去重,不然会报错
.filter(s -> "女".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0],//表示键,姓名在0索引上
s -> Integer.parseInt(s.split("-")[2])));//表示值,年龄在2索引上
System.out.println(newMap);
}
}
三、注意事项
1、 Stream操作应该避免使用可变状态或副作用,因为Stream操作可能会并行执行。
2、Stream操作的结果可能会依赖于源数据的顺序,特别是在进行排序、去重等操作时。
3、尽量避免在Stream管道中创建复杂的Lambda表达式或方法引用,以保持代码的清晰和可读性。