类集自从JDK1.2开始引入以后,一开始给予我们的特征是动态对象数组,后来有了泛型(JDK1.5),让类集操作更加的安全,再后来到JDK1.8时代,类集又变为了重要的批量数据处理的容器,而这个容器的操作实现就是利用数据流完成的。
所以JDK1.8提供了专门的数据流工具包:java.util.stream。而在这个包里面最需要关注的是Stream接口,此接口是BaseStream的子接口。
在BaseStream接口里面会包含有:DoubleStream,IntStream,LongStream,Stream<T>

Stream接口的实例化

使用Collection接口来实例化Stream接口

要观察Stream接口使用,先观察Collection接口。在Collection接口里面定义了如下方法,可取得Stream接口的实例化对象:default Stream<E> stream()

public class TestDemo {
    public static void main(String[] args) {
        List<String> all=new ArrayList<String>();
        all.add("android");
        all.add("java");
        all.add("c++");
        Stream<String> steam=all.stream();//将集合变成数据流
        System.out.println(steam.count());//取得数据流的长度
    }
}

通过如上代码,可以清楚:Collection集合里面已经支持了Steam接口对象的取得。这一点可以为集合的数据分析带来帮助。

使用Stream接口的of()方法来实例化

除了Collection集合可以为Stream接口实例化外,还可以利用Stream接口里面提供的方法完成:

public class TestDemo {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("zcc","bbg","zbq");
        stream.forEach(System.out::println);
    }
}

现在可以利用Collection接口实例化Stream接口,也可以利用Stream接口里面的of()方法,利用static方法取得Stream接口对象,但是感觉他就像集合个数的取得(Collection接口的size()方法)以及集合的输出(Iterator)

利用Stream接口来进行数据处理

断言操作,用到Stream的filter()方法

Java8之后的最大特征是支持了数据的分析操作,所以有了Stream接口对象最大的好处是可以进行集合的处理。在函数式接口里面有一个Predicate,这个接口可以负责断言操作。

public class TestDemo {
    public static void main(String[] args) {
        List<String> prop=new ArrayList<String>();
        prop.add("android");
        prop.add("java");
        prop.add("c++");
        prop.add("python");
        filter(prop, s->s.contains("a"));
    }
    public static void filter(List<String> temp, Predicate<String> pre){
        temp.forEach((s)->{
            if(pre.test(s)){
                System.out.println("data = "+s);
            }
        });
    }
}
程序输出
data = android
data = java

感觉上和Iterator没区别,但是属于Java8的输出模式。但是这种代码看起来很乱,相当于结合两个函数式接口。下面有更简单的:

public class TestDemo {
    public static void main(String[] args) {
        List<String> prop=new ArrayList<String>();
        prop.add("android");
        prop.add("java");
        prop.add("c++");
        prop.add("python");
        prop.stream().filter(s->s.contains("a")).forEach(System.out::println);
    }
}

此时虽然简化了过滤操作,但是这种操作完全可以用Iterator输出实现。下面介绍取得过滤后的子集合:

public class TestDemo {
    public static void main(String[] args) {
        List<String> prop=new ArrayList<String>();
        prop.add("android");
        prop.add("java");
        prop.add("c++");
        prop.add("python");
        List<String> subList=prop.stream().filter(s->s.contains("a")).collect(Collectors.toList());//将满足条件的集合变为一个新的集合
        subList.forEach(System.out::println);
    }
}

数据的逐个处理,用到Stream的map()方法

除了可以进行数据的判断之外,也可以进行数据的逐个处理。
范例:将包含的字符串数据进行小写变大写的处理

//将集合里面的内容转变为大写
List<String> subList=prop.stream().map(s->s.toUpperCase()).collect(Collectors.toList());

这个时候map()方法可以将每一条数据进行处理。
范例:消除重复数据
在Stream接口里面提供了重复数据消除的方法:public Stream<T> distinct();

public class TestDemo {
    public static void main(String[] args) {
        List<String> prop=new ArrayList<String>();
        prop.add("android");
        prop.add("java");
        prop.add("java");
        prop.add("java");
        prop.add("c++");
        prop.add("c++");
        prop.add("c++");
        prop.add("python");
        List<String> subList=prop.stream().map(s->s.toUpperCase()).distinct().collect(Collectors.toList());
        subList.forEach(System.out::println);
    }
}
输出
ANDROID
JAVA
C++
PYTHON

Stream的其他判断方法

判断集合中的全部数据:public boolean allMatch(Predicate<? super T> predicate)
判断集合中任意一个数据:public boolean anyMatch(Predicate<? super T> predicate)
不匹配:public boolean noneMatch(Predicate<? super T> predicate)
范例,集合的全部信息查询:

public class TestDemo {
    public static void main(String[] args) {
        List<String> prop=new ArrayList<String>();
        prop.add("android");
        prop.add("java");
        prop.add("c++");
        prop.add("python");
        if(prop.stream().allMatch(s->s.contains("a"))){
            System.out.println("集合中的全部字母都包含a");
        }else{
            System.out.println("集合中不是全部字母都包含a");
        }

        if(prop.stream().anyMatch(s->s.contains("a"))){
            System.out.println("集合中有的字母都包含a");
        }
    }
}

在java8之前,如果匿名内部类想要访问方法中的参数,则参数前必须加上final关键字,但是从java8开始可以不加上了。

多个条件的判断

以上的判断都是单个条件,下面介绍如何使用多个条件。如果判断一定使用断言的函数接口:Predicate,在这个接口里面提供了一些链接的操作方法:

  • 与操作:default Predicate<T> and(Predicate<? super T> other)
  • 或操作:default Predicate<T> or(Predicate<? super T> other)
public class TestDemo {
    public static void main(String[] args) {
        List<String> prop=new ArrayList<String>();
        prop.add("android");
        prop.add("java");
        prop.add("c++");
        prop.add("python");
        Predicate<String> conA=s->s.contains(s);
        Predicate<String> conB=s->s.length()==4;
        prop.stream().filter(conA.and(conB)).forEach(System.out::println);
    }
}
输出java

数据的两种处理方式

实际上数据流支持两类处理方式,一类是并行处理,另一类是串行处理(默认)。可以利用如下方法改变处理:

  • 设置并行处理:public S parallel()
  • 设置串行处理:public S sequential()

并行比串行快,但是顺序会受到影响。幸运的是在java中,数据流处理并发操作完全不需要用户关心,都可以自己处理。并且可以并行串行互相切换。

prop.stream().filter(conA.and(conB)).parallel().sequential().forEach(System.out::println);

所有像map(),filter()的操作是中间操作,像collectors()、forEach()的操作一定属于结尾操作。

BaseStream的子接口

在BaseStream接口里面定义了4个子接口:Stream、IntStream、LongStream、DoubleStream,以IntStream为例。
生成整型数据流:static IntStream range(int startInclusive,int endExclusive)

public class TestDemo {
    public static void main(String[] args) {
        IntStream stream =IntStream.range(0, 30);
        stream.forEach(System.out::println);
    }
}

Stream接口可以保存各种类型,而IntStream里面只能够保存int型数据。
从java8开始,一些类也增加了改变,例如:java.util.Random,在这个类里面增加了新的方法:

  • 返回一个整型数据流public IntStream ints()
public class TestDemo {
    public static void main(String[] args) {
        new Random().ints().limit(10).forEach(System.out::println);
    }
}

随着大数据的发展,基本上java的开发也向大数据靠拢。