类集自从JDK1.2开始引入以后,一开始给予我们的特征是动态对象数组,后来有了泛型(JDK1.5),让类集操作更加的安全,再后来到JDK1.8时代,类集又变为了重要的批量数据处理的容器,而这个容器的操作实现就是利用数据流完成的。
所以JDK1.8提供了专门的数据流工具包:java.util.stream。而在这个包里面最需要关注的是Stream接口,此接口是BaseStream的子接口。
在BaseStream接口里面会包含有:DoubleStream,IntStream,LongStream,Stream<T>
要观察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接口对象的取得。这一点可以为集合的数据分析带来帮助。
除了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)
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);
}
}
除了可以进行数据的判断之外,也可以进行数据的逐个处理。
范例:将包含的字符串数据进行小写变大写的处理
//将集合里面的内容转变为大写
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
判断集合中的全部数据: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接口里面定义了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的开发也向大数据靠拢。