Stream流操作--java8

概述

Stream API是java8的新特性,用于对数据元素的处理。集合关注的是数据的存储,与内存打交道,而Stream关注的是对数据的运算,与CPU打交道。
Stream的执行流程如下:

  • Stream的实例化
  • 一系列的中间操作(包括过滤、映射等)
  • 终止操作
    Stream具有一下特点:
  • 自己不会存储元素
  • 不会改变原对象
  • 操作时延迟执行的,即在执行终止操作时才会执行一系列的中间操作
    下面是一个Person类,后续对数据元素的操作涉及到该类的实例化对象
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Person {
    public String name;
    public Integer age;
    public double salary;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Double.compare(person.salary, salary) == 0 &&
                Objects.equals(name, person.name) &&
                Objects.equals(age, person.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, salary);
    }
}

Stream的实例化

Stream的实例化可以分为四种方式:

  • 通过集合创建
  • 通过数组创建
  • 通过Stream的of()方法创建
  • 创建无限流

通过集合

初始化数据:

List<Person> list = new ArrayList<>();
list.add(new Person("张三",18,3143.20));
list.add(new Person("李四",32,10941.43));
list.add(new Person("王五",45,6439.23));
list.add(new Person("钱六",28,9333.40));
  1. 调用Collection接口的默认方法default Stream<E> stream()获取顺序流
Stream<Person> stream = list.stream();
  1. 调用Collection接口的默认方法default Stream<E> parallelStream()获取并行流
Stream<Person> parallelStream = list.parallelStream();

并行流与顺序流的区别在于,顺序流由当前线程按顺序对元素进行操作;并行流内部以多线程的方式对元素进行操作。

通过数组

通过调用Arrays类的static <T> Stream<T> stream(T[] array)方法创建Stream

//int
int[] arr = new int[]{1,2,3,4,5};
IntStream stream1 = Arrays.stream(arr);
//Person
Person u1 = new Person("张三", 45, 5312.67);
Person u2 = new Person("李四", 45, 5312.67);
Person[] list = {u1, u2};
Stream<Person> stream2 = Arrays.stream(list);

对于int、double、long三种类型数组会直接创建对应的流,而自定义的数据通过泛型表示对应的流

通过Stream.of()方法

直接调用Stream的of()静态方法创建流

@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

可以看到其本质是调用了Arrays.stream()
使用示例如下:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

创建无限流

调用Stream类的iterate()和generate()可用于创建无限流,这两种方法一般用于造数据而不是对数据进行处理

  1. iterate迭代
//偶数
Stream<Integer> iterate = Stream.iterate(4, t -> t + 2);
//遍历前十个
iterate.limit(3).forEach(System.out::println);

其中第一个参数为初始值,后续对此值进行+2处理得到一个流。第二行涉及中间操作和终止操作,后续会介绍。作用为打印前10条数据
结果如下:

4
6
8
  1. generate生成
//生成随机数
Stream<Double> generate = Stream.generate(Math::random);
//打印前三个
generate.limit(3).forEach(System.out::println);

示例生成随机数,参数Math::random为方法引用。后续打印前三条数据结果如下:

0.8008846067367479
0.16378517069415766
0.21271395557491268

中间操作

中间操作包括筛选与切片、映射、排序

筛选与切片

准备数据:

ArrayList<Person> list = new ArrayList<>();
list.add(new Person("张三",18,3143.20));
list.add(new Person("李四",32,10941.43));
list.add(new Person("王五",45,6439.23));
list.add(new Person("钱六",28,9333.40));

测试相关中间操作,包括过滤、截断、跳过、去重

//filter(Predicate p) 接收lambda,从流中排除某些元素
//过滤薪水超过7000的
list.stream().filter(p -> p.getSalary() > 7000).forEach(System.out::println);

System.out.println("-----------------------------");

//limit(n) 截断流,使其元素数量不超过指定数量
//取前两个元素
list.stream().limit(2).forEach(System.out::println);

System.out.println("-----------------------------");

//skip(n) 跳过元素,返回一个扔掉前n个元素的流。若流中元素不足n个,则返回一个空流
//跳过前三个元素
list.stream().skip(3).forEach(System.out::println);

System.out.println("-----------------------------");

//distinct() 筛选,通过流所生成的元素的hashCode()和equals()去除重复元素
//新增一条重复数据后去重
list.add(new Person("张三",18,3143.20));
list.stream().distinct().forEach(System.out::println);

结果如下:

Person(name=李四, age=32, salary=10941.43)
Person(name=钱六, age=28, salary=9333.4)
-----------------------------
Person(name=张三, age=18, salary=3143.2)
Person(name=李四, age=32, salary=10941.43)
-----------------------------
Person(name=钱六, age=28, salary=9333.4)
-----------------------------
Person(name=张三, age=18, salary=3143.2)
Person(name=李四, age=32, salary=10941.43)
Person(name=王五, age=45, salary=6439.23)
Person(name=钱六, age=28, salary=9333.4)

映射

映射包括map()方法和flatMap()方法
map(Function f)接收一个函数作为参数,将元素转换为其他形式或提取信息,该函数会被应用到每个元素并将其映射成一个新元素

List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
//将每个元素映射为大写
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);

结果如下:

AA
BB
CC
DD

flatMap(Function f)接收一个函数作为参数,将流中每个值都换成另一个流,然后将所有的流连接成一个流。即如果流对象中的元素仍然是流,则将元素中的流展开。

//将一个字符串转化为流,操作元素为字符
public Stream<Character> stringToStream(String s){
	 ArrayList<Character> list = new ArrayList<>();
	 for (char c : s.toCharArray()) {
	     list.add(c);
	 }
	 return list.stream();
}

将上述list中的每个字符串转化为流,如果想要打印所有的元素使用flatMap函数如下:

Stream<Character> characterStream = list.stream().flatMap(str -> stringToStream(str));
characterStream.forEach(System.out::println);

可以看到返回的流的泛型为Character,这是因为将字符串转化的流展开了,连接为了一个流
打印结果如下:

a
a
b
b
c
c
d
d

如果使用map()函数则需要先遍历内层流,然后才能遍历流中的元素:

Stream<Stream<Character>> streamStream = list.stream().map(str -> stringToStream(str));
streamStream.forEach(s -> {
    s.forEach(System.out::println);
});

排序

排序可分为自然排序sort()和定制排序sort(Comparator c)
自然排序的元素需要实现Comparable接口,按照接口规则进行排序
定制排序则根据自己的比较器Comparator进行排序

//sorted() 自然排序
List<Integer> list = Arrays.asList(12, 43, 56, 25, 86, 35, 21);
list.stream().sorted().forEach(System.out::println);

结果如下:

12
21
25
35
43
56
86
//sorted(Comparator com) 定制排序
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("张三",18,3143.20));
list1.add(new Person("李四",32,10941.43));
list1.add(new Person("王五",45,6439.23));
list1.add(new Person("钱六",28,9333.40));
//按照年龄从小到大排序
list1.stream().sorted((p1,p2)-> p1.getAge()-p2.getAge()).forEach(System.out::println);

定制排序结果如下:

Person(name=张三, age=18, salary=3143.2)
Person(name=钱六, age=28, salary=9333.4)
Person(name=李四, age=32, salary=10941.43)
Person(name=王五, age=45, salary=6439.23)

终止操作

需要说明只有执行了终止操作,中间操作才会执行,即前述延迟执行
终止操作包括匹配与查找、归约、收集

匹配与查找

准备数据:

ArrayList<Person> list = new ArrayList<>();
list.add(new Person("张三",18,3143.20));
list.add(new Person("李四",32,10941.43));
list.add(new Person("王五",45,6439.23));
list.add(new Person("钱六",28,9333.40));

方法较为简单,全部一起测试
测试allMatch、anyMatch、noneMatch、findFirst、findAny、count、max、min、forEach

//allMatch(Predicate p) 检查是否匹配所有元素
//所有人年龄大于20,是返回true,否返回false
boolean allMatch = list.stream().allMatch(p -> p.getAge() > 20);
System.out.println(allMatch);

//anyMatch(Predicate p) 检查是否至少匹配一个元素
//存在任何一个人薪资大于10000,是返回true,否返回false
boolean anyMatch = list.stream().anyMatch(p -> p.getSalary() > 10000);
System.out.println(anyMatch);

//noneMatch(Predicate p) 检查是否没有匹配的元素
//没有人年龄小于12,是返回true,否返回false
boolean noneMatch = list.stream().noneMatch(p -> p.getAge() < 12);
System.out.println(noneMatch);

//findFirst 返回第一个元素
Optional<Person> first = list.stream().findFirst();
System.out.println(first);

//findAny 返回当前流中的任意元素
Optional<Person> any = list.stream().findAny();
System.out.println(any);

//count 返回流中元素的总数
long count = list.stream().count();
System.out.println(count);

//max(Comparator c) 返回流中最大值
//返回薪资最大的人
Optional<Person> max = list.stream().max((p1, p2) -> (int) (p1.getSalary() - p2.getSalary()));
System.out.println(max);

//min(Comparator c) 返回流中最小值
//返回年龄最小的人
Optional<Person> min = list.stream().min((p1, p2) -> p1.getAge() - p2.getAge());
System.out.println(min);

//forEach(Consumer c) 内部迭代
list.stream().forEach(System.out::println);

结果如下:

false
true
true
Optional[Person(name=张三, age=18, salary=3143.2)]
Optional[Person(name=张三, age=18, salary=3143.2)]
4
Optional[Person(name=李四, age=32, salary=10941.43)]
Optional[Person(name=张三, age=18, salary=3143.2)]
Person(name=张三, age=18, salary=3143.2)
Person(name=李四, age=32, salary=10941.43)
Person(name=王五, age=45, salary=6439.23)
Person(name=钱六, age=28, salary=9333.4)

归约

归约指reduce操作,将集合中的元素合并为一个最后结果

//reduce(T identity,BinaryOperator) 可以将流中元素反复结合起来,得到一个值。返回T
//初始值为1,求1~10的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer reduce = list.stream().reduce(1,(i1,i2) -> i1+i2);
System.out.println(reduce);

//reduce(BinaryOperator) 可以将流中元素反复结合起来,得到一个值,返回Optional<T>
//求1~10的和
Optional<Integer> reduce1 = list.stream().reduce((i1, i2) -> i1 + i2);
System.out.println(reduce1);

运行结果如下:

56
Optional[55]

收集

collectct(Collector c)将流转化为其他的形式,常常是列表或集合。接收一个Collector接口的实现,用于装载元素
用法如下:

//准备数据
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("张三",18,3143.20));
list.add(new Person("李四",32,10941.43));
list.add(new Person("王五",45,6439.23));
list.add(new Person("钱六",28,9333.40));
//集合汇总
List<Person> collect = list.stream().filter(p -> p.getSalary() > 6000).collect(Collectors.toList());
collect.forEach(System.out::println);

可以看到collect之后是一个list,如果想要set,将collect参数改为Collectors.toSet()即可

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值