java8新特性学习(三) Stream

一、Stream简介

Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤映射数据等操作。
使用Stream API对集合数据进行操作,就类似于使用SQL语句执行数据库查询一样。还可以使用Stream来并行执行操作。

二、什么是Stream

是数据渠道,用于操作数据源(集合、数组)所生成的元素序列
“集合讲的是数据,流讲的是计算”

注意:

  1. Stream自己不会存储元素
  2. Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
  3. Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

三、Stream执行的三个步骤

  • 创建Stream
  • 中间操作
  • 终止操作

1.创建Stream:
一个数据源,获取一个流对象

以下是创建Stream的实例

public class TestStreamAPI01 {

    //创建Stream
    public static void main(String[] args) {
        //1.可以通过Collection系列集合提供的stream()或parallelStream
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();

        //2.通过Arrays中的静态方法stream()获取数组流
        Integer[] nums= new Integer[10];
        Stream<Integer> personStream = Arrays.stream(nums);
        //3.通过Stream类中的静态方法
        Stream<String> stringStream = Stream.of("a", "b", "c", "d");
        //4.创建无限流
        //迭代,Stream的iterate()方法
        Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
        //生产,Stream的generate()方法
        Stream.generate(() -> Math.random())
    }
}

2.中间操作:

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
下面列出项目中经常用到的方法。

①筛选和切片

方法描述
filter(Predicate p)接收Lambda,从流中排除某些元素
limit(long maxSize)截断流,使其元素不超过指定数量
skip(long n)跳过元素,返回一个扔掉了前n个元素的流。若流中的元素不足n个,则返回一个空流。与limit互补
distinct筛选,通过流所生成的hashCode()和equals()去除重复元素

下面准备了一个用于测试各种方法的实体类Person

public class Person {

    private String name;
    private Integer age;
    private int money;
    private Status status;

    public Person() {
    }

    public Person(String name, Integer age, int money, Status status) {
        this.name = name;
        this.age = age;
        this.money = money;
        this.status = status;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

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

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

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

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

    public enum Status {
        FREE,
        BUSY,
        VOCATION;
    }
}

创建一个Person集合

    private List<Person> personList = Arrays.asList(
            new Person("迪迦", 18, 2000),
            new Person("盖亚", 28, 3000),
            new Person("戴拿", 38, 4000),
            new Person("塞罗", 48, 5000),
            new Person("塞文", 30, 6000),
            new Person("塞文", 30, 6000),
            new Person("塞文", 30, 6000)
    );

filter过滤数据

	//内部迭代,迭代过程由StreamAPI内部完成
    @Test
    public void test1() {
        //中间操作:不会执行任何操作
        Stream<Person> personStream = personList.stream().
                filter(x -> {
                    System.out.println("中间操作");
                    return x.getAge() > 24;
                });
        //终止操作:一次性执行全部操作,即“惰性求值”
        personStream.forEach(System.out::println); 
    }
    //外部迭代
    @Test
    public void test2() {
        Iterator<Person> iterator = personList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

输出结果

中间操作
中间操作
中间操作
中间操作
Person{name=‘塞罗’, age=48, money=5000, status=null}
中间操作
中间操作
中间操作

limit限定获取元素的数量

    personList.stream()
            .filter((x) -> {
                System.out.println("短路");
                return x.getMoney() > 1000;
            })
            .limit(2)//获取两个元素,当满足条件,将不执行后面的迭代操作
            .forEach(System.out::println);

输出结果

短路
Person{name=‘迪迦’, age=18, money=2000, status=null}
短路
Person{name=‘盖亚’, age=28, money=3000, status=null}

skip设置跳过的元素数量
distinct去除重复元素

    personList.stream()
            .filter((x) -> {
                        System.out.println("过滤");
                        return x.getMoney() > 3000;
                    }
            )
            .skip(2)    //跳过前两个元素,取后面的元素
            .distinct() //去除重复元素,要求对象必须实现hashCode()方法和equals方法()
            .forEach(System.out::println);
过滤
过滤
过滤
过滤
过滤
Person{name='塞文', age=30, money=6000, status=null}
过滤
Person{name='塞文', age=30, money=6000, status=null}
过滤
Person{name='塞文', age=30, money=6000, status=null}

② 映射:接收一个Lambda,将元素转换成其他形式或提取信息。

方法描述
map接受一个函数作为参数,该函数会被应用到每个元素上并将其映射成一个新元素。
flatMap接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

map

    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
    list.stream()
            .map(String::toLowerCase)
            .forEach(System.out::println);
    personList.stream()
            .map(Person::getAge)
            .forEach(System.out::println);

aaa
bbb
ccc
ddd
18
28
38
48
30
30
30

flatMap与map的使用对比

    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (char c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }
		//flatMap
       System.out.println("----------------");
       Stream<Character> stream = list.stream()
               .flatMap(TestStreamAPI02::filterCharacter);
       stream.forEach(System.out::println);
       //map
       Stream<Stream<Character>> streamStream = list.stream()
               .map(TestStreamAPI02::filterCharacter);
       streamStream.forEach((characterStream -> {
           characterStream.forEach(System.out::println);
       }));

map是将数据转换为类似[{a,a,a},{b,b,b},{c,c,c}]这样的格式,而flatMap将数据转化为[a,a,a,b,b,b,c,c,c]的格式

③ 排序

方法描述
sorted()自然排序(Comparable)
sorted(Comparator com)定制排序(Comparator)
	List<String> list = Arrays.asList("ccc", "ddd", "fff", "xxx");
	list.stream()
	        .sorted()
	        .forEach(System.out::println);
	
	System.out.println("自然排序----------");
	personList.stream()
	        .sorted(((o1, o2) -> {
	            if (o1.getAge().equals(o2.getAge())) {  //判断相邻两个年龄是否相同
	                return o1.getName().compareTo(o2.getName());    //相同就按名字排序
	            }else {
	                return o1.getAge().compareTo(o2.getAge());  //不同就按年龄排
	            }
	        }))
	        .forEach(System.out::println);
	System.out.println("定制排序----------");
    }

自然排序是根据对象类型中所实现Comparable接口的compareTo方法来排序。如此例中String类型就实现了Comparable接口的方法。
在这里插入图片描述
输出结果

ccc
ddd
fff
xxx
自然排序----------
Person{name=‘迪迦’, age=18, money=2000, status=null}
Person{name=‘盖亚’, age=28, money=3000, status=null}
Person{name=‘塞文’, age=30, money=6000, status=null}
Person{name=‘塞文’, age=30, money=6000, status=null}
Person{name=‘塞文’, age=30, money=6000, status=null}
Person{name=‘戴拿’, age=38, money=4000, status=null}
Person{name=‘塞罗’, age=48, money=5000, status=null}
定制排序----------

3.终止操作
终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void

① 查找与匹配

方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count()返回流中元素的总个数
max(Comparator c)返回流中的最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代(使用Collection接口需要用户去做迭代,称为外部迭代。相反,StreamAPI使用内部迭代——它帮你把迭代做了)

在Person类中新增一个enum,增加属性和构造方法

 public enum Status {
        FREE,
        BUSY,
        VOCATION;
    }

因为Person新增了新的属性,重新创建一个集合

    private List<Person> personList = Arrays.asList(
            new Person("迪迦", 18, 2000, Person.Status.FREE),
            new Person("盖亚", 28, 3000, Person.Status.BUSY),
            new Person("戴拿", 38, 4000, Person.Status.VOCATION),
            new Person("塞罗", 48, 5000, Person.Status.BUSY),
            new Person("塞文", 30, 6000, Person.Status.VOCATION),
            new Person("塞文", 30, 6000, Person.Status.FREE),
            new Person("塞文", 30, 6000, Person.Status.BUSY)
    );

以下实例将把表格中出现的方法都一一测试

    @Test
    public void test1() {
        boolean b1 = personList.stream()
                .allMatch(person -> person.getStatus().equals(Person.Status.BUSY));
        System.out.println("allMatch()----->" + b1);
        boolean b2 = personList.stream()
                .anyMatch(person -> person.getStatus().equals(Person.Status.BUSY));
        System.out.println("anyMatch()----->" + b2);
        boolean b3 = personList.stream()
                .noneMatch(person -> person.getStatus().equals(Person.Status.BUSY));
        System.out.println("noneMatch()---->" + b3);
        Optional<Person> op = personList.stream()
                .sorted((o1, o2) -> Integer.compare(o1.getMoney(), o2.getMoney()))
                .findFirst();
        System.out.println("findFirst()---->" + op.get());
        Optional<Person> any = personList.stream()
                .filter(person -> person.getStatus().equals(Person.Status.VOCATION))
                .findAny();
        System.out.println("findAny()---->" + any.get());
    }

输出结果:

allMatch()----->false
anyMatch()----->true
noneMatch()---->false
findFirst()---->Person{name=‘迪迦’, age=18, money=2000, status=FREE}
findAny()---->Person{name=‘迪迦’, age=18, money=2000, status=FREE}

@Test
    public void test2() {
        long count = personList.stream()
                .count();
        System.out.println("count()---->" + count);
        Optional<Person> max = personList.stream()
                .max((o1, o2) -> Integer.compare(o1.getAge(), o2.getAge()));
        System.out.println("max()---->" + max.get());
        Optional<Person> min = personList.stream()
                .min((o1, o2) -> Integer.compare(o1.getMoney(), o2.getMoney()));
        System.out.println("min()---->" + min.get());
        Optional<Integer> min1 = personList.stream()
                .map(Person::getMoney)
                .min(Integer::compare);
        System.out.println(min1.get());
    }

输出结果:

count()---->7
max()---->Person{name=‘塞罗’, age=48, money=5000, status=BUSY}
min()---->Person{name=‘迪迦’, age=18, money=2000, status=FREE}
2000

② 归约

方法描述
reduce(T identity,BinaryOperator)可以将流中元素反复结合起来,得到一个新值,返回T
reduce(BinaryOperator)可以将流中元素反复结合起来,得到一个新值,返回Optional< T>

两个参数的,第一个参数指定元素的起始值,下面实例参数为0,也就是从1开始计算

        //求1~10的和
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer nums = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(nums);
        //先使用map方法获取Person对象中的money属性,再统计出money的总和
        Optional<Integer> op = personList.stream()
                .map(Person::getMoney)
                .reduce(Integer::sum);
        System.out.println(op.get());

55
32000

③ 收集
collect——将流转换为其他形式。接受一个Collector接口的实现,用于Stream中元素做汇总的方法

collect使用的各种实例

//转换为list
        List<String> list = personList.stream()
                .map(Person::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("----------");
        //转换为set,将重复名字去除
        Set<String> set = personList.stream()
                .map(Person::getName)
                .collect(Collectors.toSet());
        set.forEach(System.out::println);

        System.out.println("--------");
        //转换为HashSet
        HashSet<String> hashSet = personList.stream()
                .map(Person::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);
		//总数
	    Long aLong = personList.stream()
	            .collect(Collectors.counting());
	    System.out.println("总数:" + aLong);
	    //年龄平均值
	    Double aver = personList.stream()
	            .collect(Collectors.averagingDouble(Person::getAge));
	    System.out.println("平均值:" + aver);
	    //工资总和
	    IntSummaryStatistics moneyCount = personList.stream()
	            .collect(Collectors.summarizingInt(Person::getMoney));
	    System.out.println("总和:" + moneyCount);
	    //工资最大值
	    Optional<Person> max = personList.stream()
	            .collect(Collectors.maxBy((o1, o2) -> Integer.compare(o1.getMoney(), o2.getMoney())));
	    System.out.println("最大值" + max.get());
	    //最小值
	    Optional<Person> min = personList.stream()
	            .collect(Collectors.minBy((o1, o2) -> Integer.compare(o1.getMoney(), o2.getMoney())));
	    System.out.println("最小值:" + min.get());
	    //按照状态分组
	    Map<Person.Status, List<Person>> map = personList.stream()
	            .collect(Collectors.groupingBy(Person::getStatus));
	    map.forEach((key, value) -> {
	        System.out.println("状态:" + key);
	        System.out.println("分组:" + value);
	    });

输出结果

总数:7
平均值:31.714285714285715
总和:IntSummaryStatistics{count=7, sum=32000, min=2000, average=4571.428571, max=6000}
最大值Person{name=‘塞文’, age=30, money=6000, status=VOCATION}
最小值:Person{name=‘迪迦’, age=18, money=2000, status=FREE}
状态:VOCATION
分组:[Person{name=‘戴拿’, age=38, money=4000, status=VOCATION}, Person{name=‘塞文’, age=30, money=6000, status=VOCATION}]
状态:FREE
分组:[Person{name=‘迪迦’, age=18, money=2000, status=FREE}, Person{name=‘塞文’, age=30, money=6000, status=FREE}]
状态:BUSY
分组:[Person{name=‘盖亚’, age=28, money=3000, status=BUSY}, Person{name=‘塞罗’, age=48, money=5000, status=BUSY}, Person{name=‘塞文’, age=30, money=6000, status=BUSY}]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值