JAVA8新特性之Stream API(一)

除了Lambda表达式,JAVA8还出现了一些新特性来配合Lambda表达式进行简化编程,如函数式接口,今天要分享的是JAVA8又一大特性——Stream API,这是一个方便我们对数据操作的新特性。
所谓Stream,从字面理解是“流”的意思,个人理解是它充当的是一个类似管道的中间者,通过将数据在一端接收过来,进行一系列我们需要的操作,最后将结果从另外一端输出,成为我们最后需要的数据。由于是起中间作用,Stream本身不会存储元素,也不会改变数据源,而只有当我们需要结果的时候,stream才会执行
Stream操作数据大致分为三个步骤
一是创建Stream
二是进行中间操作
三是终止操作
今天主要说的是创建Stream以及中间操作的几种方式:

创建Stream

有这样四种方式:
1.通过collection系列集合提供的Stream()方法创建串行流或是通过parallelStream() 方法创建串行流

		List<String> list=new ArrayList<>();
		//Stream()创建串行流
        Stream<String> stream1=list.stream();
        //parallelStream() 创建并行流
        Stream<String> stream2=list.parallelStream();

串行指的是一个线程做完所有事情,存在一个先后顺序,而并行流指的是多个线程做一件事。

2.通过Arrays中的静态方法stream()获取数组流

		Student[] stus=new Student[10];
        Stream<Student> stream2=Arrays.stream(stus);

3.通过Stream类中的静态方法 of()

Stream<String> stream3=Stream.of("aa","bb","cc");

4.创建无限流
无限流的两种形式:迭代和生成
迭代:

		Stream<Integer> stream4=Stream.iterate(0,(x) -> x+2);
        stream4.limit(10).forEach(System.out::println);

查看iterate()的源码,发现它需要两个参数

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

变量名为seed(种子)的参数,指的是迭代的开始值,而后面的UnaryOperator接口,继承自函数型接口Function,用于对初始值进行一系列操作,比如上面的代码中,我们对初始值0进行了10次自增2的操作,并进行输出

生成:

Stream.generate(()->Math.random())
              .limit(5)
              .forEach(System.out::println);*/

        }

查看generate()方法源代码,发现:

public static<T> Stream<T> generate(Supplier<T> s)

参数是一个供给型接口,会返回一个类型为T的值

那么说完了如何创建Stream,我们来看看Stream如何进行中间操作的

中间操作

Stream的中间操作可以大致分为筛选与切片、排序以及映射等等,而筛选与切片又包含了几种方式,本次主要说的是筛选与切片
在筛选与切片中,存在四种常用的方式:
filter():过滤,接收一个lambda表达式,丛流中排除元素
limit(n):截断流,使其元素不超过指定的值,有点像mysql中的limit限制查询
skip(n):它与limit是一个互补的关系,用于跳过n个元素,返回第n个元素之后满足条件的元素,若流中的元素不足n个,则会返回一个空流
distinct():筛选,通过流生成元素的hashCode()和equals()去除重复元素

接下来我们逐个用简单的例子来进行演示:
首先是filter过滤:
假设我们有一个类型为Student对象的List集合,Student类的属性如下:

	private Integer id;//编号
    private String name;//姓名
    private Integer age;//年龄
    private Double height;//身高
List<Student> stus= Arrays.asList(
            new Student(001,"张三",16,168.25),
            new Student(002,"李四",48,178.25),
            new Student(007,"赵六",18,180.68),
            new Student(003,"王五",12,148.45)
    );

查看filter()方法的源码:

Stream<T> filter(Predicate<? super T> predicate);

发现该方法是一个参数为断言型接口,返回值为Stream类型的方法

假如我们需要得到集合中年龄大于或等于35岁的学生:

public void  test1(){
        // 中间操作:不会执行任何操作
            Stream<Student> stream=stus.stream()
                .filter((e)->{
                    System.out.println("Stream API的中间操作");
                    return e.getAge()>=35;
                });
        stream.forEach((e)-> System.out.println(JSONObject.toJSON(e)));
        }

首先我们创建一个类型为Student的Stream,通过filter()方法传入一个lambda表达式进行判断,为了让过程更加清晰,我们加上一个输出语句,最后使用终止操作forEach循环输出满足条件的对象,查看forEach方法的源码:

void forEach(Consumer<? super T> action);

发现该方法其实是在循环对一个消费型接口进行操作,在这里我们其实可以使用方法引用的方式进行输出内存地址,更为简洁,但为了方便观察,我们还是将传入的对象进行JSON处理,最后得出的结果:
在这里插入图片描述
从结果不难看出,我们并没有对filter方法中传入的参数e进行迭代处理,但Stream API自动帮我们完成迭代操作,而如果我们不写最后的终止操作forEach,将不会输出一句“Stream API的中间操作”,也就是说明,只有终止操作存在时才会执行所有的内容,我们称这种方法为“惰性求值”

当然我们也可以使用传统的外部迭代方式:

@Test
    public void  test2(){
        Iterator<Student> iterator=stus.iterator();
        while (iterator.hasNext()){
            if (iterator.next().getAge()>=35){
                System.out.println(iterator.next());
            }
        }
    }

接下来是截断流limit,这里我们将筛选filter也加入

比如我们现在的student集合存在多个同样的学生“张三”信息,而我们需要得到两个身高大于160cm的学生信息。
我们先查看limit方法的源码:
limit:

Stream<T> limit(long maxSize);

传入一个长整作为最大值,返回一个自定义类型的Stream

接下来完成我们的功能:

@Test
    public  void test3(){
        stus.stream()
                .filter((e)->{
                    System.out.println("短路!");
                    return e.getHeight()>=160;
                })
                .limit(2)
                .forEach((e)-> System.out.println(JSONObject.toJSON(e)));
    }

运行结果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200301134752533.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ZqdGRfMjAxOQ==
集合中我们可以看到,满足身高大于160的学生一共有三位,而使用limit(2)后,只输出两条信息,同样的,该集合中有4个student对象,但Stream API只进行了2次迭代,这与短路操作符&& ||存在同样的效果。

接下来我们看看skip跳过操作,同样是上面的功能,我们将limit(2)替换成skip(2):

@Test
    public void test4(){
        stus.stream()
                .filter((e)->e.getHeight()>=160)
                .skip(2)
                .forEach((e)-> System.out.println(JSONObject.toJSON(e)));
    }

运行结果如下:
在这里插入图片描述
输出了上面我们limit(2)后第3个满足条件的学生信息

最后我们将筛选distinct()放入上面的limit例子中。注意,因为distinct去重是根据元素的hashCode以及equals方法,所以我们需要在实体类中重写这两个方法:

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return getId().equals(student.getId()) &&
                getName().equals(student.getName()) &&
                getAge().equals(student.getAge()) &&
                getHeight().equals(student.getHeight());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getName(), getAge(), getHeight());
    }

比如我们的Student集合现在是这样:

List<Student> stus= Arrays.asList(
            new Student(001,"张三",16,168.25),
            new Student(001,"张三",16,168.25),
            new Student(001,"张三",16,168.25),
            new Student(001,"张三",16,168.25),
            new Student(002,"李四",48,178.25),
            new Student(007,"赵六",18,180.68),
            new Student(003,"王五",12,148.45)

    );

存在4个相同的“张三”,并且都满足身高大于160的条件,我们查看distinct方法的源码
distinct:

Stream<T> distinct();

没有任何参数,只返回一个自定义类型的Stream

@Test
    public  void test3(){
        stus.stream()
                .filter((e)->{
                    System.out.println("短路!");
                    return e.getHeight()>=160;
                })
                .distinct()
                .limit(2)
                .forEach((e)-> System.out.println(JSONObject.toJSON(e)));
    }

加入distinct后,我们看看运行结果:
在这里插入图片描述
一共迭代5次,分别是前四个相同的“张三”以及“李四”,我们限制了元素为2,所以不会继续向下迭代

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值