Stream流
概述:
Stream流式思想类似于工厂车间的生产流水线,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。
为什么使用Stream流:
集合操作数据弊端很明显,每次都要循环遍历,还要创建新集合,很麻烦。
集合弊端:
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
/**
* 1、拿到所有姓张的,2.拿到名字长度为三个字的,3.打印
*/
List<String> nameList = new ArrayList<>();
for (String name : list) {
if (name.startsWith("张")) {
nameList.add(name);
}
}
List<String> threeList = new ArrayList<>();
for (String name : nameList) {
if (name.trim().length() == 3){
threeList.add(name);
}
}
threeList.forEach(s -> {
System.out.println(s);
});
Stream流操作:
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
/**
* 1、拿到所有姓张的,2.拿到名字长度为三个字的,3.打印
*/
list.stream().filter(s -> {
return s.startsWith("张");
}).filter(s -> {
return s.length()==3;
}).forEach(s -> {
System.out.println(s);
});
获取Stream流的两种方式
- 通过Collection接口中的默认方法Stream stream()
- 通过Stream接口中的静态of方法
/**
* 方式一、根据Collection获取流
* default Stream<E> stream()
* 因为是 default修饰 所以只要实现Collection接口的类都可使用
*/
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Set<String> set = new HashSet<>();
set.stream();
Map<String, Object> map = new HashMap<>();
Stream<Map.Entry<String, Object>> stream1 = map.entrySet().stream();
Stream<Object> stream2 = map.values().stream();
Stream<String> stream3 = map.keySet().stream();
/**
* 方式二、Stream中的静态方法of获取流
* static<T> Stream<T> of(T... values)
* 可变参,本质就是数组
*/
Stream<String> stream4 = Stream.of("aa", "bb", "cc");
String[] strs = {"aa", "bb", "cc"};
Stream<String> stream5 = Stream.of(strs);
/**
* 基本数据类型的数据行不行??
* 不行!!!!! stream会将整个数组看做一个元素进行操作
*/
int[] arr = {11,22,33};
Stream<int[]> stream6 = Stream.of(arr); //不行
Stream流注意事项
- Stream流只能操作一次
- Stream调用非终结方法返回的是新的流
- Stream流最后不调用终结方法,中间的操作不会执行.
Stream流常用方法
方法名 | 方法作用 | 返回值类型 | 方法种类 |
---|---|---|---|
count | 统计个数 | long | 终结 |
forEach | 逐一处理 | void | 终结 |
filter | 过滤 | Stream | 函数拼接(非终结) |
limit | 取用前几个 | Stream | 函数拼接 |
skip | 跳过前几个 | Stream | 函数拼接 |
map | 映射 | Stream | 函数拼接 |
concat | 组合 | Stream | 函数拼接 |
**终结:**返回类型不是Stream
**非终结:**返回类型是Stream
forEach方法 逐一处理
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
list.stream().forEach(s->{
System.out.println(s);
});
/**
* 换成 方法引用写法
*/
list.stream().forEach(System.out::println);
count方法 统计个数
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
System.out.println(list.stream().count());
filter方法 过滤
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
System.out.println(list.stream().filter(s -> {
return s.startsWith("张");
}).count());
limit方法 取前几个
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
/**
* 取集合前几个,这里取前两个
* "张无忌", "周芷若"
*/
list.stream().limit(2).forEach(s->{
System.out.println(s);
});
skip方法 跳过前几个
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
/**
* 跳过前几个.
* "李逍遥", "张三丰"
*/
list.stream().filter(s -> {
return s.length() == 3;
}).skip(2).forEach(s -> {
System.out.println(s);
});
map方法 映射类型转
List<String> list = new ArrayList<>();
// Collections.addAll(list, "张无忌", "周芷若", "李逍遥", "张三丰", "张三");
Collections.addAll(list, "111", "222", "333", "44", "55");
/**
* map 映射可以将一种类型的流转换成另一种类型的流
*/
/*Stream<Integer> integerStream = list.stream().map(s -> {
return Integer.parseInt(s);
});*/
list.stream().map(s -> {
return Integer.parseInt(s);
}).forEach(s->{
System.out.println(s);
});
sorted 方法 排序
-
Stream<T> sorted(); //默认排序升序 Stream<T> sorted(Comparator<? super T> comparator); //自定义比较器
Stream<Integer> stream = Stream.of(11, 22, 44, 33, 66, 55);
/**
* 默认排序(升序)
*/
stream.sorted().forEach(System.out::println);
/**
* 指定比较器(降序)
*/
stream.sorted((o1, o2) -> {
return o2 - o1;
}).forEach(System.out::println);
distinct方法 去重
Stream<Integer> stream = Stream.of(11, 22, 44, 33, 66, 55,11);
stream.distinct().forEach(System.out::println);
/**
* 去重类 ,这个类需要重写 equals和hashCode方法
*/
Stream<Person> stream = Stream.of(
new Person("张三", 15),
new Person("张三", 16),
new Person("张三", 17),
new Person("张三", 15)
);
stream.distinct().forEach(System.out::println);
match方法 匹配指定条件
-
boolean allMatch(Predicate<? super T> predicate); //匹配所有元素,所有元素都满足返回true boolean anyMatch(Predicate<? super T> predicate); //匹配某个元素,只要有一个元素满足返回true boolean noneMatch(Predicate<? super T> predicate); //匹配所有元素,所有元素都不满足返回true
Stream<Integer> stream = Stream.of(11, 22, 44, 33, 66, 55,11);
/**
* allMatch 匹配所有元素,所有元素都满足返回true
*/
boolean b = stream.allMatch(s -> {
return s > 20;
});
System.out.println(b);
/**
* anyMatch 匹配某个元素,只要有一个元素满足返回true
*/
boolean b = stream.anyMatch(s -> {
return s > 20;
});
System.out.println(b);
/**
* noneMatch 匹配所有元素,所有元素都不满足返回true
*/
boolean b = stream.noneMatch(s -> {
return s > 20;
});
System.out.println(b);
fina方法 找第一个元素
-
Optional<T> findFirst(); //找第一个元素 Optional 意味着可能找到可能找不到 Optional<T> findAny(); //找第一个元素
Stream<Integer> stream = Stream.of(11, 22, 44, 33, 66, 55,11);
/**
* Optional 意味着可能找到可能找不到
*/
Optional<Integer> streamFirst = stream.findFirst();
System.out.println(streamFirst.get());
Optional<Integer> any = stream.findAny();
System.out.println(any.get());
max和min方法 最大值和最小值获取
Stream<Integer> stream = Stream.of(11, 22, 44, 33, 66, 55,11);
/**
* 取最大 反之取最小,
*/
/*Optional<Integer> max = stream.max((o1, o2) -> {
return o1 - o2;
});*/
//简化写法
Optional<Integer> max = stream.max(Comparator.comparingInt(o -> o));
System.out.println(max.get());
Stream<Integer> stream = Stream.of(11, 22, 44, 33, 66, 55,11);
/**
* 取最小
*/
Optional<Integer> min = stream.min((o1, o2) -> {
return o1 - o2;
});
System.out.println(min.get());
reduce方法 归纳数据得到结果
Stream<Integer> stream = Stream.of(1,2,3);
/**
* T reduce(T identity, BinaryOperator<T> accumulator);
* T identity:默认值
* BinaryOperator<T> accumulator:对数据处理的方式
*/
//取和
Integer reduce = stream.reduce(0, (x, y) -> {
return x + y;
});
System.out.println(reduce); //6 0+1+2+3
//取最大值
Integer reduce = stream.reduce(0, (x, y) -> {
return x > y ? x : y;
});
System.out.println(reduce); //3 // 0 : 1 1:2 2 : 3
map结合reduce使用
Stream<Person> stream = Stream.of(
new Person("张三", 1),
new Person("张三", 2),
new Person("张三", 3),
new Person("张三", 4)
);
// 求年龄总和
Integer reduce = stream.map(p -> {
return p.getAge();
}).reduce(0, (x, y) -> {
return x + y;
});
System.out.println(reduce);
// 求最大年龄
Integer reduce = stream.map(p -> {
return p.getAge();
}).reduce(0, (x, y) -> {
return Math.max(x,y);
});
System.out.println(reduce);
Stream<String> stream1 = Stream.of("a","b","c","a","a");
//统计a出现的次数
Integer a = stream1.map(s -> {
if (s.equals("a")) {
return 1;
} else {
return 0;
}
}).reduce(0, (x, y) -> {
return Integer.sum(x, y);
});
System.out.println(a);
mapInt方法 将Integer类型Stream流转为IntStream
// Integer占用内存比int多,在Stream流操作中会自动装箱拆箱
Stream<Integer> stream = Stream.of(1, 2, 3);
// IntStream 内部操作int类型数据,减少内存,减少自动装箱拆箱
IntStream intStream = stream.mapToInt(s -> {
return s.intValue();
});
intStream.forEach(System.out::println);
concat方法 合并两个流为一个流
-
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
Stream<Integer> integerStream = Stream.of(1, 2, 3);
Stream<String> stringStream = Stream.of("1", "2", "3");
Stream<? extends Serializable> stream = Stream.concat(integerStream, stringStream);
stream.forEach(System.out::println);
收集Stream流中的结果
收集为集合collect
Stream<Integer> stream = Stream.of(1, 2, 3,2);
//收集到list集合中
List<Integer> list = stream.filter(s -> {
return s > 1;
}).collect(Collectors.toList());
//收集到set中
Set<Integer> set = stream.filter(s -> {
return s > 1;
}).collect(Collectors.toSet());
//收集到指定集合中 set同样
ArrayList<Integer> arrayList = stream.filter(s -> {
return s > 1;
}).collect(Collectors.toCollection(ArrayList::new));
HashSet<Integer> hashSet = stream.filter(s -> {
return s > 1;
}).collect(Collectors.toCollection(HashSet::new));
收集数组toArray
Stream<String> stream = Stream.of("aa","bb","vv");
//转成Object数组 操作元素不方便
Object[] objects = stream.toArray();
System.out.println(Arrays.toString(objects));
//转成原有类型
String[] strings = stream.toArray(String[]::new);
System.out.println(Arrays.toString(strings));
对流中数据进行聚合计算
Stream<Person> stream = Stream.of(
new Person("张三", 2),
new Person("李四", 5),
new Person("王五", 3)
);
//求最大值
Optional<Person> collect = stream.collect(Collectors.maxBy(((o1, o2) -> {
return o1.getAge() - o2.getAge();
})));
System.out.println("最大值:"+collect.get());
//求最小值
Optional<Person> collect = stream.collect(Collectors.minBy(((o1, o2) -> {
return o1.getAge() - o2.getAge();
})));
System.out.println("最小值:"+collect.get());
//求和
Integer collect = stream.collect(Collectors.summingInt(s -> s.getAge()));
System.out.println("总和:"+collect);
//求平均值
Double collect = stream.collect(Collectors.averagingInt(s -> s.getAge()));
System.out.println("平均值:"+collect);
//统计数量
Long collect = stream.collect(Collectors.counting());
System.out.println("数量:"+collect);
对流中数据进行分组(按条件分组)
Stream<Person> stream = Stream.of(
new Person("张三", 18),
new Person("李四", 5),
new Person("赵六", 5),
new Person("王五", 20)
);
//根据age分组
Map<Integer, List<Person>> map = stream.collect(Collectors.groupingBy(s -> s.getAge()));
map.forEach((k,v)->{
System.out.println("键:"+k+":::值:"+v);
});
//根据age分成年和未成年
Map<String, List<Person>> map = stream.collect(Collectors.groupingBy(s -> {
if (s.getAge() >= 18) {
return "成年";
} else {
return "未成年";
}
}));
map.forEach((k,v)->{
System.out.println("键:"+k+":::值:"+v);
});
对流中数据进行多级分组
Stream<Person> stream = Stream.of(
new Person("张三", 20,65),
new Person("李四", 5,65),
new Person("赵六", 5,88),
new Person("王五", 20,88)
);
//先根据 age分组,在根据score分组
Map<Integer, Map<Integer, List<Person>>> map = stream.collect(Collectors.groupingBy(s -> s.getAge(), Collectors.groupingBy(s -> s.getScore())));
map.forEach((k,v)->{
System.out.println(k);
v.forEach((k2,v2)->{
System.out.println("\t"+k2+"===="+v2);
});
});
/*20
65====[Person(name=张三, age=20, score=65)]
88====[Person(name=王五, age=20, score=88)]
5
65====[Person(name=李四, age=5, score=65)]
88====[Person(name=赵六, age=5, score=88)]*/
对流中数据进行分区
- 满足条件是一个分区,不满足是另外一个分区
//partitioningBy 满足条件true组 不满足 false组
Map<Boolean, List<Person>> map = stream.collect(Collectors.partitioningBy(s -> {
return s.getScore() > 60;
}));
map.forEach((k,v)->{
System.out.println(k+"===="+v);
});
/*false====[Person(name=张三, age=20, score=55), Person(name=李四, age=5, score=58)]
true====[Person(name=赵六, age=5, score=88), Person(name=王五, age=20, score=88)]*/
对流中数据进行拼接
- Collectors.joining 会根据指定的连接符,将所有元素连接成一个字符串
Stream<Person> stream = Stream.of(
new Person("张三", 20,55),
new Person("李四", 5,58),
new Person("赵六", 5,88),
new Person("王五", 20,88)
);
String collect = stream.map(s -> s.getName()).collect(Collectors.joining());
System.out.println(collect); //张三李四赵六王五
String collect = stream.map(s -> s.getName()).collect(Collectors.joining("----"));
System.out.println(collect); //张三----李四----赵六----王五
String collect = stream.map(s -> s.getName()).collect(Collectors.joining("-","KK_","EE"));
System.out.println(collect); //KK_张三-李四-赵六-王五EE
并行的Stream流
- 串行Stream流 , 就是在一个线程上执行
Stream.of(1,2,3).filter(s->{
System.out.println(Thread.currentThread()+"==="+s);
return s > 1;
}).count();
/* Thread[main,5,main]===1
Thread[main,5,main]===2
Thread[main,5,main]===3*/
- 获取并行流的两种方式
List<String> list = new ArrayList<>();
//方式一: 直接获取并行的Stram流
Stream<String> stringStream = list.parallelStream();
//方式二: 将串行流转成并行流
Stream<String> parallel = list.stream().parallel();
- 并行流,不同的线程调用 效果
Stream.of(1,2,3).parallel()
.filter(s->{
System.out.println(Thread.currentThread()+"==="+s);
return s > 1;
}).count();
/* Thread[ForkJoinPool.commonPool-worker-2,5,main]===3
Thread[main,5,main]===2
Thread[ForkJoinPool.commonPool-worker-9,5,main]===1 */
并行Stream流线程安全问题
- 线程不安全的情况
List<Integer> list = new ArrayList<>();
IntStream.rangeClosed(1,1000)
.parallel()
.forEach(s->{
list.add(s);
});
System.out.println("集合大小::"+list.size()); //950 946
- 解决方案一: synchronized
List<Integer> list = new ArrayList<>();
Object obj = new Object();
IntStream.rangeClosed(1,1000)
.parallel()
.forEach(s->{
synchronized (obj){
list.add(s);
}
});
System.out.println("集合大小::"+list.size()); //集合大小::1000
2. 解决方案二: 使用线程安全的集合
//使用线程安全的集合一
Vector vector = new Vector();
IntStream.rangeClosed(1,1000)
.parallel()
.forEach(s->{
vector.add(s);
});
System.out.println("集合大小::"+vector.size()); //集合大小::1000
//使用线程安全的集合二
List<Integer> list = new ArrayList<>();
List<Integer> synchronizedList = Collections.synchronizedList(list);
IntStream.rangeClosed(1,1000)
.parallel()
.forEach(s->{
synchronizedList.add(s);
});
System.out.println("集合大小::"+synchronizedList.size()); //集合大小::1000
3.解决方案三: 使用stream流的收集collect(线程安全)
List<Integer> collect = IntStream.rangeClosed(1, 1000)
.parallel()
.boxed() //先装箱
.collect(Collectors.toList());
System.out.println("集合大小::"+collect.size()); //集合大小::1000