1。Steam流式思想概述
注意:Stream和IO流(InputStream/OutputStream)没有任何关系。
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约。
2.Stream流的获取方式
2.1根据Collection获取
java.util.Collection 接口中加入了default方法 stream,也就是说Collection接口下的所有的实现都可以通过steam方法来获取Stream流。
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.stream();
Set<String> set=new HashSet<>();
set.stream();
Vector<String> vector=new Vector<>();
vector.stream();
//Map接口别没有实现Collection接口,这时我们可以根据Map获取对应的keyvalue的集合。
Map<String,Object> map = new HashMap<>();
Stream<String> stream = map.keySet().stream(); // key
Stream<Object> stream1 = map.values().stream(); // value
Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); // entry
}
2.2通过Stream的of方法
public static void main(String[] args) {
getStream2();
}
private static void getStream2(){
String [] strArr={"1","a","b"};
Stream.of(strArr).forEach(System.out::println);
Integer [] arr={1,2,3,4,5};
Stream.of(arr).forEach(System.out::println);
int [] arr2={1,2,3,4};
Stream.of(arr2).forEach(System.out::println);
}
控制台输出:
1
2
3
4
5
[I@404b9385
1
a
b
可以看出基本数据类型的数组是不能通过Stream.of()获取到Stream的。
2.3Stream的常用方法
Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。
终结方法包括:
count 、forEach 等。
非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结方法。)
Stream注意事项
- Stream只能操作一次
- Stream方法返回的是新的流
- Stream不调用终结方法,中间的操作不会执行
2.3.1 forEach(用来遍历流中的数)
接口:
void forEach(Consumer<? super T> action);
该方法接受一个Consumer接口,会将每一个流元素交给函数处理
使用:
public static void main(String[] args) {
forEachTest();
}
/**
* 循环遍历
*/
private static void forEachTest(){
List<String> strings = Arrays.asList("a", "b", "c");
strings.forEach(System.out::println);
}
控制台输出:
a
b
c
2.3.2 count(统计元素个数)
接口:
long count();
使用:
public static void main(String[] args) {
countTest();
}
private static void countTest(){
long count = Stream.of(1, 2, 3, 4, 5).count();
System.out.println("Stream中的元素个数为:"+count);
}
控制台输出:
Stream中的元素个数为:5
2.3.3 filter(过滤数据返回符合条件的数据)
接口:
Stream<T> filter(Predicate<? super T> predicate);
接收一个Predicate函数式接口参数作为筛选条件,可以通过filter方法将一个流转换成另一个子集流。
使用:
public static void main(String[] args) {
filterTest();
}
private static void filterTest(){
Stream.of(1,2,3,4,5).filter(s->s>2).forEach(System.out::println);
}
控制台输出:
3
4
5
2.3.4 limit(对流进行截取处理,截取前n个数据)
接口:
Stream<T> limit(long maxSize);
使用:
public static void main(String[] args) {
limitTest();
}
private static void limitTest(){
Stream.of(1,2,3,4,5).limit(2).forEach(System.out::println);
}
控制台输出:
1
2
2.3.5 skip(从第n位开始截取数据)
接口:
Stream<T> skip(long n);
使用:
public static void main(String[] args) {
skipTest();
}
private static void skipTest(){
Stream.of(1,2,3,4,5).skip(2).forEach(System.out::println);
}
控制台输出:
3
4
5
2.3.6 map(要将流中的元素映射到另一个流中)
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
使用:
public static void main(String[] args) {
mapTest();
}
private static void mapTest(){
Stream.of("a","b").map(String::toUpperCase).forEach(System.out::println);
}
控制台输出:
A
B
2.3.7 sorted (排序)
接口:
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
使用:
public static void main(String[] args) {
sortedTest();
}
private static void sortedTest(){
//按自然顺序排序
Stream.of(1, 4, 2, 5, 3).sorted().forEach(System.out::println);
//自定义排序
Stream.of(1,4,2,5,3).sorted((o1, o2) -> o2-o1).forEach(System.out::println);
}
控制台输出:略
2.3.7 distinct(去重复)
接口
public static void main(String[] args) {
distinctTest();
}
private static void distinctTest(){
Stream.of("a","b","a","c").distinct().forEach(System.out::println);
Stream.of(
new UserDto(1l,"张三"),
new UserDto(1l,"张三"),
new UserDto(3l,"李四"),
new UserDto(4l,"王五")
).distinct().forEach(System.out::println);
}
控制台输出:
a
b
c
UserDto{id=1, userName=‘张三’}
UserDto{id=1, userName=‘张三’}
UserDto{id=3, userName=‘李四’}
UserDto{id=4, userName=‘王五’}
这时候可以看到我们自定义的类型是没有过滤到重复值的,我们需要重写hashCode和equals方法来去重,即:
UserDto 重写hashCode和equals方法:
public class UserDto {
private Long id;
private String userName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public UserDto(Long id, String userName) {
this.id = id;
this.userName = userName;
}
@Override
public String toString() {
return "UserDto{" +
"id=" + id +
", userName='" + userName + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserDto userDto = (UserDto) o;
return Objects.equals(id, userDto.id) && Objects.equals(userName, userDto.userName);
}
@Override
public int hashCode() {
return Objects.hash(id, userName);
}
}
测试
public static void main(String[] args) {
distinctTest();
}
private static void distinctTest(){
Stream.of("a","b","a","c").distinct().forEach(System.out::println);
Stream.of(
new UserDto(1l,"张三"),
new UserDto(1l,"张三"),
new UserDto(3l,"李四"),
new UserDto(4l,"王五")
).distinct().forEach(System.out::println);
}
控制台输出:
a
b
c
UserDto{id=1, userName=‘张三’}
UserDto{id=3, userName=‘李四’}
UserDto{id=4, userName=‘王五’}
2.3.8 match(判断数据是否匹配指定的条件)
接口:
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
使用:
public static void main(String[] args) {
matchTest("张");
}
private static void matchTest(String msg){
boolean b = Stream.of("张", "王", "李").anyMatch(s -> s.equals(msg));
System.out.println("元素中是否有任意一个满足条件是否成立:"+b);
boolean b1 = Stream.of("张", "王", "李").allMatch(s -> s.equals(msg));
System.out.println("元素中都满足条件是否成立:"+b1);
boolean b2 = Stream.of("张", "王", "李").noneMatch(s -> s.equals(msg));
System.out.println("元素中都不满足条件是否成立:"+b2);
}
控制台输出:
元素中是否有任意一个满足条件是否成立:true
元素中都满足条件是否成立:false
元素中都不满足条件是否成立:false
2.3.9 find(查找某些元素)
接口:
Optional<T> findFirst();
Optional<T> findAny();
使用:
public static void main(String[] args) {
findTest();
}
private static void findTest() {
String s1 = Stream.of("张三", "张三飞", "李四", "王五")
.filter(msg->msg.startsWith("张"))
.findFirst().get();
String s2 = Stream.of("张三", "张三飞", "李四", "王五")
.filter(msg->msg.startsWith("张"))
.findAny().get();
System.out.println("s1:"+s1+"---s2:"+s2);
}
这时候会发现,findFirst与findAny返回的都是第一个元素的结果,但是不符合API给出的理论,按道理讲findAny应该是返回其中的任意一个,这时候发现了流有:
串行流:在一个线程在处理业务。
并行流:是会有多个线程来并发处理业务。
那么我们来用并行流测试一下:
public static void main(String[] args) {
findTest();
}
private static void findTest() {
List<String> list1 = Arrays.asList("张三", "张三飞", "李四", "王五");
String s3=list1.parallelStream().filter(msg->msg.startsWith("张"))
.findFirst().get();
String s4=list1.parallelStream().filter(msg->msg.startsWith("张"))
.findAny().get();
System.out.println("并行流操作 s3:"+s3+"---s4:"+s4+"");
String s1 = Stream.of("张三", "张三飞", "李四", "王五")
.filter(msg->msg.startsWith("张"))
.findFirst().get();
String s2 = Stream.of("张三", "张三飞", "李四", "王五")
.filter(msg->msg.startsWith("张"))
.findAny().get();
System.out.println("串行流操作 s1:"+s1+"---s2:"+s2);
}
控制台输出:
并行流操作 s3:张三—s4:张三飞
串行流操作 s1:张三—s2:张三
所以得出结论,如果是串行流的话,findAny是返回第一个,如果是并行流会返回任意一个。
2.3.10 max和min (最大值、最小值)
接口:
Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);
使用:
public static void main(String[] args) {
maxAndMinTest();
}
private static void maxAndMinTest(){
Integer max = Stream.of(1, 2, 3, 4, 5).max((o1, o2) -> o1 - o2).get();
System.out.println("最大值:"+max);
Integer min = Stream.of(1, 2, 3, 4, 5).min((o1, o2) -> o1 - o2).get();
System.out.println("最小值:"+min);
}
控制台输出:
最大值:5
最小值:1
2.3.11 reduce (数据归纳得到一个数据)
接口:
T reduce(T identity, BinaryOperator<T> accumulator);
使用:
reduceTest();
}
private static void reduceTest(){
//reduce(0, (x, y) -> x + y)
//0 是一个默认值
//第一次的时候会将默认值赋给x,后续是将上一次操作的结果赋值给x
// Integer reduce = Stream.of(1, 2, 3, 4, 5).reduce(0, (x, y) -> x + y);
Integer reduce = Stream.of(1, 2, 3, 4, 5)
.reduce(0, (x, y) ->{
System.out.println("x:"+x+" y"+y);
return x + y;
});
System.out.println(reduce);
}
控制台输出:
x:0 y1
x:1 y2
x:3 y3
x:6 y4
x:10 y5
15
2.3.12 concat(将两个流合并成一个流)
接口:
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
使用:
public static void main(String[] args) {
concatTest();
}
private static void concatTest(){
Stream<String> i = Stream.of("1", "2");
Stream<String> a = Stream.of("a", "b");
Stream.concat(i,a).forEach(System.out::println);
}
控制台输出:
1
2
a
b