java8新特性之集合流式操作

特点

  1. 集合的流式操作,不是一个数据结构,不负责任何的数据存储;
  2. 它更像是一个迭代器,我们可以有序的获取到数据源中的每一个数据,并且可以对这些数据进行一些操作;
  3. 流式操作的每一个方法,返回值都是返回流的本身。

步骤

  1. 获取数据源:数据源可以是集合、数组 …
  2. 对数据进行处理:过滤、排序、映射 … (中间操作)
  3. 对流中数据进行整合:转成集合、数量 … (最终操作)

一、最终操作

讲解
1.准备一个实体类,注意这个实体的setter方法都是有返回值的

public class Person {
    private String name;
    private int age;
    private int score;

    public String getName() {
        return name;
    }

    public Person setName(String name) {
        this.name = name;
        return this;
    }

    public int getAge() {
        return age;
    }

    public Person setAge(int age) {
        this.age = age;
        return this;
    }

    public int getScore() {
        return score;
    }

    public Person setScore(int score) {
        this.score = score;
        return this;
    }

    public Person() {
    }

    public Person(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

2.准备一个存储数据的类,需要使用数据时通过getData()方法获取

public class Data {
    public static ArrayList<Person> getData() {
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("xiaoming", 10, 100));
        list.add(new Person("xiaoli", 12, 90));
        list.add(new Person("xiaowang", 9, 80));
        list.add(new Person("xiaozhang", 11, 70));
        list.add(new Person("xiaomei", 7, 95));
        list.add(new Person("xiaohei", 13, 60));
        list.add(new Person("xiaohei", 13, 60));
        return list;
    }
}

3.使用类

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Program {
    public static void main(String[] args) {
        //获取数据源
        Stream<Person> s = Data.getData().stream();
        //Stream.of(Data.getData());

        //最终操作1:collect
        //Collectors工具类
        //转成List
        //List<Person> list = s.collect(Collectors.toList());
        //System.out.println(list);
        //转成Set
        //Set<Person> set = s.collect(Collectors.toSet());
        //System.out.println(set);
        //转成Map
        //Map<String, Integer> map = s.collect(Collectors.toMap(ele -> ele.getName(), ele -> ele.getScore()));
        //Map<String, Integer> map = s.collect(Collectors.toMap(Person::getName, Person::getScore));
        //System.out.println(map);

        //最终操作2:reduce
        //reduce的思想是n1+n2得到的和再与n3相加,相加之后得到的值再与n4相加,以此类推直到最后一个数据相加完毕
        //Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        //Optional<Integer> ret1 = s1.reduce((n1, n2) -> n1 + n2); //对若干个数字求和
        //System.out.println(ret1.get());
        //求Person中的总成绩
        //Person tmp = new Person();//作为一个中间值,两个数求和的结果存在这个临时对象中
        //Optional<Person> ret = s.reduce((n1, n2) -> tmp.setScore(n1.getScore() + n2.getScore()));
        //System.out.println(ret.get().getScore());

        //最终操作3: max 和 min
        //需求1:找到集合中成绩最高的人的信息
        //Person max = s.max((ele1, ele2) -> ele1.getScore() - ele2.getScore()).get();
        //System.out.println(max);
        //需求2:找到集合中年龄最小的人的信息
        //Person min = s.min((ele1, ele2) -> ele1.getAge() - ele2.getAge()).get();
        //System.out.println(min);

        //最终操作4: match(anyMatch、allMatch、noneMatch)
        //需求1:判断集合中是否包含成绩不合格的学员
        //boolean ret = s.anyMatch(ele -> ele.getScore() < 60);
        //System.out.println(ret);
        //需求2:判断集合中是否包含成绩大于80的学员
        //boolean ret = s.anyMatch(ele -> ele.getScore() > 80);
        //System.out.println(ret);
        //需求3:判断集合中是否所有的学员都及格
        //boolean ret = s.allMatch(ele -> ele.getScore() > 60);
        //System.out.println(ret);
        //boolean ret = s.noneMatch(ele -> ele.getScore() < 60);//集合中不包含小于60的学员则返回true
        //System.out.println(ret);

        //最终操作5:count
        //需求:求元数据中有多少个元素
        //long count = s.count();
        //System.out.println(count);

        //最终操作6:forEach
        //s.forEach(ele -> System.out.println(ele));
        s.forEach(System.out::println);
    }
}

注意事项
一个 Stream 只可以使用一次,之所以叫最终操作是因为流在使用一次后就会关闭,关闭之后就不能再使用这个流进行其他的操作了。

二、中间操作

讲解
1.Person实体类改动:
(1)重写两个方法:equals()和hashCode(),用于去重方法的验证;
(2)实现Comparable接口中的compareTo方法,来实现一个自定义排序的依据,用于排序方法的验证。

public class Person implements Comparable<Person> {
    private String name;
    private int age;
    private int score;

    public String getName() {
        return name;
    }

    public Person setName(String name) {
        this.name = name;
        return this;
    }

    public int getAge() {
        return age;
    }

    public Person setAge(int age) {
        this.age = age;
        return this;
    }

    public int getScore() {
        return score;
    }

    public Person setScore(int score) {
        this.score = score;
        return this;
    }

    public Person() {
    }

    public Person(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                score == person.score &&
                Objects.equals(name, person.name);
    }

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

    @Override
    public int compareTo(Person o) {
        return this.getScore() - o.getScore(); //按照成绩进行一个排序
    }
}

2.使用类

public class Process {
    public static void main(String[] args) {
        //中间操作1:filter
        //filter是一个过滤器,可以自定义一个过滤条件,将流中满足条件的元素保留
        //它返回的是一个Stream,所以拿到结果后还可以在进行其他操作
        //需求:保留这个集合中成绩大于等于的学员信息
        Data.getData().stream().filter(ele -> ele.getScore() >= 80).forEach(System.out::println);

        //中间操作2:distinct
        //distinct:去重,去除集合中重复的元素
        //去重规则:
        // 1.先判断hashCode()
        // 2.如果hashCode相同,再判断equals()
        // 3.如果先判断hashCode()和equals()返回都是true,才会去重成功
        Data.getData().stream().distinct().forEach(System.out::println);

        //中间操作3:sorted
        //sorted:排序,对流中的元素进行排序
        //sorted()无参使用,要求流中的元素对应的类需要实现 Comparable 接口,
        //并且去实现Comparable中的compareTo方法,来实现一个自定义排序的依据或者大小比较的依据
        Data.getData().stream().sorted().forEach(System.out::println);
        //sorted(Comparator<T> c):使用自定义的规则来进行排序。在实现Comparable情况下,优先使用有参的这个排序
        Data.getData().stream().sorted((ele1, ele2) -> ele1.getAge() - ele2.getAge()).forEach(System.out::println);

        //中间操作4:limit
        //limit:限制,只取流中前指定位的元素
        Data.getData().stream().limit(3).forEach(System.out::println);//获取前三个元素

        //中间操作5:skip
        //skip:跳过
        Data.getData().stream().skip(3).forEach(System.out::println);//跳过前三个元素

        //一般情况下limit和skip是配合使用的,limit和skip返回的都是Stream
        Data.getData().stream().skip(1).limit(4).forEach(System.out::println);//获取第二个到第五个元素

        //中间操作6:map
        //map:元素映射,用指定的元素来替换掉流中的元素
        //需求1:将流中的Person对象替换成他们的姓名
        Data.getData().stream().map(ele -> ele.getName()).forEach(System.out::println);
        //需求2:将流中成绩小于80的Person对象替换成他们的姓名
        Data.getData().stream().map(ele -> ele.getScore() >= 80 ? ele : ele.getName()).forEach(System.out::println);

        //中间操作7:flatMap
        String[] array = {"hello", "world"};
        //需求:获取所有的字符:List -> h,e,l,l,o,w,o,r,l,d
        System.out.println(Arrays.stream(array).map(ele -> ele.split("")).flatMap(Arrays::stream).collect(Collectors.toList()));
    }
}

三、综合运用

import java.util.List;
import java.util.stream.Collectors;

public class Exercise {
    public static void main(String[] args) {
        /**
         * 需求:一个集合中存储了若干个Person对象,要求查询出以下结果:
         *
         * 1.所有几个的学生信息
         * 2.所有及格的学生姓名
         * 3.班级的前3名(按照成绩)
         * 4.班级的3-6名(按照成绩)
         * 5.所有不及格的学生的平均成绩
         * 6.将及格的学生,按照成绩降序输出所有信息
         * 7.班级学生的总分
         */

        // 1.所有几个的学生信息
        List<Person> list1 = Data.getData().stream().filter(ele -> ele.getScore() >= 60).collect(Collectors.toList());

        // 2.所有及格的学生姓名
        List<String> names = Data.getData().stream().filter(ele -> ele.getScore() >= 60).map(ele -> ele.getName()).collect(Collectors.toList());

        // 3.班级的前3名(按照成绩)
        List<Person> list2 = Data.getData().stream().sorted((ele1, ele2) -> ele2.getScore() - ele1.getScore()).limit(3).collect(Collectors.toList());

        // 4.班级的3-6名(按照成绩)
        List<Person> list3 = Data.getData().stream().sorted((ele1, ele2) -> ele2.getScore() - ele1.getScore()).skip(2).limit(4).collect(Collectors.toList());

        // 5.所有不及格的学生的平均成绩
        Person tmp = new Person();
        Data.getData().stream().filter(ele -> ele.getScore() < 60).reduce((ele1, ele2) -> tmp.setScore(ele1.getScore() + ele2.getScore()));
        long count = Data.getData().stream().filter(ele -> ele.getScore() < 60).count();
        float ave = tmp.getScore() / (float) count;

        // 6.将及格的学生,按照成绩降序输出所有信息
        Data.getData().stream().filter(ele -> ele.getScore() >= 60).sorted((ele1, ele2) -> ele2.getScore() - ele1.getScore()).forEach(System.out::println);

        // 7.班级学生的总分
        Data.getData().stream().reduce((ele1, ele2) -> tmp.setScore(ele1.getScore() + ele2.getScore()));
        System.out.println(tmp.getScore());
    }
}

四、并行流

什么是并行流?

并行流可以把流中的一些数据分成若干个块,然后用不同的线程来操作不同块的数据,并且并行流已经对我们的临界资源做好了一些处理,我们基本上不用担心多个线程访问相同资源需要面临的问题。

为什么要去使用并行流呢?

并行流由于引用了线程,我们可以进行并发的处理,从而提高一些效率和节省一些时间。

import java.util.stream.LongStream;

public class ParallelStream {
    public static void main(String[] args) {
        //获取并行流的方式
        //Data.getData().stream().parallel()
        //Data.getData().parallelStream()

        //获取一个LongStream,这里边存储了若干个long型数据
        //LongStream.rangeClosed(0L, 10L);//11个数据
        //LongStream.rangeClosed(0L, 10L).forEach(System.out::println);

        //串行流
        //串行大概耗时 19661 ms
        long start = System.currentTimeMillis();
        LongStream.rangeClosed(0L, 50000000000L).reduce(Long::sum);//求和
        long end = System.currentTimeMillis();
        System.out.println("串行耗时:" + (end - start) + " ms");

        //并行流
        //并行大概耗时 9100 ms
        long start2 = System.currentTimeMillis();
        LongStream.rangeClosed(0L, 50000000000L).parallel().reduce(Long::sum);//求和
        long end2 = System.currentTimeMillis();
        System.out.println("并行耗时:" + (end2 - start2) + " ms");
    }
}

并行流对于处理一些庞大的数据它所耗费的时间是比较短的,使用并行流一定程度上节省了运行时间。

有了并行流的概念我们再补充个最终操作:

public class Program {
    public static void main(String[] args) {
        // ... ...

        //最终操作7:findFirst 和 findAny
        //findFirst:获取流中的第一个元素,无论是并行流还是串行流,取的都是流中的第一个元素
        System.out.println(Data.getData().parallelStream().findFirst());
        System.out.println(Data.getData().stream().findFirst());//使用串行流效果是一摸一样的
        //findAny:获取流中的任意一个元素,对于串行流结果等同于findFirst,并行流中则不是
        //在并行流的环境下,使用findAny比findFirst效率要高
        System.out.println(Data.getData().stream().findAny());
        System.out.println(Data.getData().parallelStream().findAny());
    }
}

五、Collectors工具类

Collectors是一个工具类,提供了若干个方法,返回一个Collectors接口的实现类对象。在最终操作中有简单的使用了Collectors的toList()、toSet()、toMap(),我们再来看下其他常用的几个方法。

import java.util.IntSummaryStatistics;
import java.util.stream.Collectors;

public class CollectorsDemo {
    public static void main(String[] args) {
        //maxBy 和 minBy
        //maxBy:通过指定的规则,获取流中最大的元素
        //minBy:通过指定的规则,获取流中最小的元素
        System.out.println(Data.getData().stream().collect(Collectors.maxBy((ele1, ele2) -> ele1.getScore() - ele2.getScore())));
        System.out.println(Data.getData().stream().collect(Collectors.minBy((ele1, ele2) -> ele1.getAge() - ele2.getAge())));

        //joining:合并,将流中的元素,以字符串的形式拼接起来
        //需求:将集合中所有的Person对象的姓名拼接成一个字符串
        //无参,没有分隔符直接拼接
        String ret1 = Data.getData().stream().map(Person::getName).collect(Collectors.joining());
        System.out.println(ret1);
        //一个参,分隔符(逗号拼接)
        String ret2 = Data.getData().stream().map(Person::getName).collect(Collectors.joining(","));
        System.out.println(ret2);
        //三个参,分别为拼接符、前缀、后缀
        String ret3 = Data.getData().stream().map(Person::getName).collect(Collectors.joining(",", "[", "]"));
        System.out.println(ret3);

        //summingInt,summingDouble,summingLong
        //summingInt:计算int类型的和,将流中的每一个元素映射成int类型然后求和
        //需求:将集合中的所有人的成绩求和
        System.out.println(Data.getData().stream().collect(Collectors.summingInt(Person::getScore)));

        //averagingInt,averagingDouble,averagingLong
        //averagingInt:计算int类型的平均值,将流中的每一个元素映射成int类型然后求平均值
        System.out.println(Data.getData().stream().collect(Collectors.averagingInt(Person::getScore)));

        //summarizingInt,summarizingDouble,summarizingLong
        //summarizingInt:将流中的每一个元素映射成int类型然后获取这些元素的描述信息,对这些int类型进行了简单描述
        //输出结果为:IntSummaryStatistics{count=7, sum=555, min=60, average=79.285714, max=100}
        System.out.println(Data.getData().stream().collect(Collectors.summarizingInt(Person::getScore)));
        //返回类型为IntSummaryStatistics
        IntSummaryStatistics ret = Data.getData().stream().collect(Collectors.summarizingInt(Person::getScore));
        System.out.println(ret.getCount());//个数
        System.out.println(ret.getMax());//最大值
        System.out.println(ret.getMin());//最小值
        System.out.println(ret.getSum());//和
        System.out.println(ret.getAverage());//平均值
    }
}

over ~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值