Stream流
在JDK8引入了流的概念。本质上是为了对集合的功能进行进一步的完善。使代码更加简洁优雅,还可以并行处理。
什么是流
流,是一系列数据项,它不是一种数据结构。
流可以进行相关的算法和计算,只是它并没有显式地表现,而是在内部进行隐式地操作。
我们可以把流认为就是一条流水线,在流水线上可以进行各种筛选操作。
流的优势
流,解决了两个问题,1.直接实现你”想要什么”,2.可以进行并行处理。
流,的本质,集合Lambda表达式,对传统Java集合的功能强化。
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。
Stream流的获取
stream流的获取方式只有四种:
- 单列集合 使用collection接口的stream默认方法生成流
- 双列集合 间接获取流 可以先通过keySet或者entrySet方法获取到set集合,在获取stream流
- 数组 Arrays工具类中的静态方法stream生成流
- 同种数据类型的多个数据
1 2 3 4 5
a b c d e
这种形式的数据可以通过 Stream.of(T… values) 用以生成流。
单列集合
private static void method1() {
// 单列集合
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
// 获取流
Stream<String> stream = list.stream();
stream.forEach(str -> System.out.println(str));
}
双列集合(Map)
private static void method2() {
// 双列集合(map)
HashMap<String, String> map = new HashMap<>();
map.put("1", "a");
map.put("2", "b");
map.put("3", "c");
// 先获取到所有的键key,然后把这个set结合中所有的键放到stream中
map.keySet().stream().forEach(s -> System.out.println(s + "--->" + map.get(s)));
System.out.println("============");
// 直接获取所有的键值对对象,然后把这个集合中所有的键值对放到stream流中
map.entrySet().stream().forEach(entry -> System.out.println(entry.getKey() + "--->" + entry.getValue()));
}
数组
private static void method3() {
// 数组
int[] arr = {1, 2, 3, 4, 5};
Arrays.stream(arr).forEach(a -> System.out.println(a));
}
同种数据类型的多个数据
private static void method4() {
// 同种数据类型的多个数据
Stream.of(1, 2, 3, 4, 5).forEach(a -> System.out.println(a));
}
stream流的中间方法
1. filter
- Stream filter(Predicate predicate) 用于对流中的数据进行过滤
Predicate接口中的方法
boolean test(T t) 对给定的参数进行判断,返回一个布尔值
private static void method5() {
// 1. Stream filter(Predicate predicate) 用于对流中的数据进行过滤
// * Predicate接口中的方法
// * (1)boolean test(T t) 对给定的参数进行判断,返回一个布尔值
ArrayList<String> arrayList = new ArrayList();
arrayList.add("张三丰");
arrayList.add("张无忌");
arrayList.add("张翠山");
arrayList.add("赵六");
arrayList.add("张亮");
arrayList.add("刘备");
arrayList.add("关于");
// filter方法会获取流中的每个数据
// test方法中的s就是流中的每个数据,
// 方法返回值为true,表示这个数据留着,
// 为false,着会剔除流中的该数据
// 对数据进行判断,张开头的数据留下,
arrayList.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
if (s.startsWith("张")) return true;
return false;
}
}).forEach(str -> System.out.println(str));
System.out.println("------------");
// 接口只有一个抽象方法,所以可以使用lambda表达式替换,简化书写
arrayList.stream().filter(s -> {
// if (s.startsWith("张")) return true;
// return false;
return s.startsWith("张");
}).forEach(str -> System.out.println(str));
}
2. limit
Stream limit(long maxSize) 截取指定参数个数的数据.
打个比方,用户往流中传入了10个数据,我只要前五个,那么就可以使用该方法
private static void method6() {
// Stream limit(long maxSize) 截取指定参数个数的数据
ArrayList<String> arrayList = new ArrayList();
arrayList.add("张三丰");
arrayList.add("张无忌");
arrayList.add("张翠山");
arrayList.add("赵六");
arrayList.add("张亮");
arrayList.add("刘备");
arrayList.add("关于");
arrayList.stream().limit(3).forEach(str-> System.out.println(str));
}
3. skip
Stream skip(long n): 跳过指定参数个数的数据
打个比方,用户往流中传入了10个数据,我不要前五个,那个就可以使用该方法,直接跳过前五个数据
private static void method7() {
// Stream limit(long maxSize) 截取指定参数个数的数据
ArrayList<String> arrayList = new ArrayList();
arrayList.add("张三丰");
arrayList.add("张无忌");
arrayList.add("张翠山");
arrayList.add("赵六");
arrayList.add("张亮");
arrayList.add("刘备");
arrayList.add("关于");
arrayList.stream().skip(3).forEach(str-> System.out.println(str));
}
4. distinct
Stream distinct() 去除流中重复的元素,依赖hashCode和equals方法(自定义对象需要重写这两个方法)
private static void method8() {
// Stream limit(long maxSize) 截取指定参数个数的数据
ArrayList<String> arrayList = new ArrayList();
arrayList.add("张三丰");
arrayList.add("张无忌");
arrayList.add("张翠山");
arrayList.add("张三丰");
arrayList.add("张三丰");
arrayList.add("赵六");
arrayList.add("张亮");
arrayList.add("刘备");
arrayList.add("关于");
arrayList.add("关于");
arrayList.stream().distinct().forEach(str-> System.out.println(str));
}
5. Stream流的静态方法concat
static Stream concat(Stream a,Stream b) 合并a和b两个流
private static void method9() {
// Stream limit(long maxSize) 截取指定参数个数的数据
ArrayList<String> arrayList = new ArrayList();
ArrayList<String> arrayList2 = new ArrayList();
arrayList.add("张三丰");
arrayList.add("张无忌");
arrayList.add("张翠山");
arrayList2.add("赵六");
arrayList2.add("张亮");
arrayList2.add("刘备");
arrayList2.add("关于1");
Stream<String> stream = arrayList.stream();
Stream<String> stream2 = arrayList2.stream();
Stream<String> concat = Stream.concat(stream, stream2);
concat.forEach(str ->
System.out.println(str)
);
}
终结方法
一个stream流只能有一个终结方法
是流水线上的最后一个操作
常见的终结方法:
- void forEach(Consumer action)
Consumer接口中的方法 void accept(T t) 对给定的参数执行此操作 - long count() 返回此流中的元素个数
private static void method10() {
// Stream limit(long maxSize) 截取指定参数个数的数据
ArrayList<String> arrayList = new ArrayList();
arrayList.add("张三丰");
arrayList.add("张无忌");
arrayList.add("张翠山");
arrayList.add("赵六");
arrayList.add("张亮");
arrayList.add("刘备");
arrayList.stream().forEach(str-> System.out.println(str));
long count = arrayList.stream().count();
System.out.println(count);
}
Stream流的收集操作(终结方法)
使用Stream流的方式操作完成后,如何收集流中的数据,留着以后使用?
Stream流的收集方法:
- R collect(Collector collector)
工具类Collectors提供了具体的收集方式
- static Collector toList(): 把元素收集到List集合中
- static Collector toSet(): 把元素收集到set集合中
- static Collector toMap(Function keyMapper,Function valueMapper): 把元素收集到Map集合中
/*
* stream流的收集方法
* R collect(Collector collector)
* */
ArrayList<Integer> arrayList = new ArrayList();
for (int i = 1; i < 11; i++) {
arrayList.add(i);
}
// 只要偶数
List<Integer> list = arrayList.stream()
.filter(number -> number % 2 == 0)
// 收集流中的数据,但是不负责创建容器,也不负责把数据添加到容器中
// Collectors.toList() 在底层会创建一个List容器,并把流中的数据添加到List容器中
.collect(Collectors.toList());
System.out.println(list);
// Collectors.toSet() 在底层会创建set容器,并把流中的数据加入到容器中
Set<Integer> set = arrayList.stream().filter(number -> number % 2 != 0)
.collect(Collectors.toSet());
System.out.println(set);
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("张三,1");
arrayList.add("李四,2");
arrayList.add("王五,3");
arrayList.stream().filter(str -> {
// 获得编号,保留编号小于等于2的
String[] split = str.split(",");
return Integer.valueOf(split[1]) <= 2;
}).collect(Collectors.toMap(
// 第一个表达式,用来获取键,第二个表达式用来获取值
str->str.split(",")[0],
str->str.split(",")[1]
));
结论
**注意:**在stream流中是无法直接修改集合或数组等数据源中的数据的。