Java 1.8 两个重大的改变,第一个就是Lambda表达式 ,另一个就是Stream API.
Stream 简介
而这个添加了一个新的抽象称为Stream(java.util.stream)。其把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充。
为什么这样说?因为可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
为什么要有stream?实际开发中,项目中多数数据来源都来自Mysql,Oracle等。打死你hi现在数据源更多了,比如MongDb,Redis等,而这些Nosql的数据就需要Java层面去处理了。
- 补充1:Stream与Collection集合的区别:
- Collection 是一种静态的内存数据结构,主要面向内存,存储在内存中。
- Stream是由关计算的。主要是面向CPU,通过CPU实现计算。
- 补充2:
- Stream自己不会存储数据
- Stream不会改变源的对象。相反,它们会返回一个持有结果的新Stream。
- Stream操作是延迟执行的。这意味它们会等到结果的时候才会执行。
步骤
stream的操作有三个步骤
- 1.创建Stream:一个数据源(如 集合,数组)从而获取一个Stream<T>,泛型最好带着,因为有时候不带使用lambda的时候会有类型错误,默认其泛型为obeject的流。
- 2.中间操作 : 一个中间操作链,对数据源的数据进行处理。(代码中演示)
- 3.终止操作:一旦执行终止操作 ,就执行了中间操作链,并产生结果,之后就不会再被使用。这也是为什么说Stream是延迟执行的原因。
步骤1 生成stream的四种方式
创建方式1–通过集合
这种方式是最简单的一种创建stream的方式,直接通过集合调用方法即可得到
public class test {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
// 创建一个Stream
// 返回一个顺序操作的stream 从上倒下的操作
Stream stream1= list.stream();
// 返回一个并行操作的stream 有几个线程会同时运行,所以数据有可能不按照得到
Stream stream2= list.parallelStream();
}
}
创建方式2–通过数组
这一种是调用Arrays类的中的方法,进行创建的。
public class test {
public static void main(String[] args) {
String[] arr= {"a","b"};
Stream<String> stream=Arrays.stream(arr); // 这种也可以创建自定义的类类型 是顺序
}
}
创建方式3–of方法
Stream本身就有一个方法of()。
public class test {
public static void main(String[] args) {
Stream<String> steam=Stream.of("a","b"); // 这种也可以创建自定义的类类型 是顺序
}
}
创建方式4-无限流
这种方式,暂时先了解先看代码。
public class test {
public static void main(String[] args) {
// 无限流第一种 迭代
// 其中这个方法用到了Function<T, T> 接口的子接口 public interface UnaryOperator<T> extends Function<T, T>
// 这个地方其实也用了中间操作 limit 以及终止操作 forEach 不然无法体现出效果
Stream.iterate(0, t-> t=t+1).limit(5).forEach(System.out::println);
}
}
public class test {
public static void main(String[] args) {
// 无限流第二种 生成
// 其中这个方法用到了Supplier<T> public static<T> Stream<T> generate(Supplier<T> s) {
// 这个地方其实也用了中间操作 limit 以及终止操作 forEach 不然无法体现出效果
// 普通写法
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
// TODO Auto-generated method stub
return Math.random();
}
}).limit(10).forEach(System.out::println);
// 方法引用
// Supplier<T> 实现的方法中一行代码 通过 Math用random方法,所以可以用方法引用
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
步骤2 中间操作
中间操作说白了就是对数据进行处理
代码演示前,首先创建一个对象类:
class ObJ{
private String name;
private int age;
public ObJ(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
// 为了更方便看输出的数据所以重写一下String
public String toString() {
// TODO Auto-generated method stub
return "名字是:"+this.name+" 年纪:"+this.age;
}
}
再创建一个带有对象的list:
List<ObJ> list=new ArrayList<ObJ>();
list.add(new ObJ("张三",15));
list.add(new ObJ("李四",9));
list.add(new ObJ("王五",14));
list.add(new ObJ("赵六",5));
list.add(new ObJ("王七",25));
list.add(new ObJ("张八",15));
ObJ obj=new ObJ("石九", 13);
list.add(obj);
list.add(obj);
所以后面代码演示的时候直接就用list了,不然每次重复,会让文章变得又长又没有多少干货。
筛选与切片
常用方法如下:
方法 | 作用 |
filter(Predicate p) | 接受一个lambda,从stream中按照要求得得到数据 |
limit(n) | 截断数据,是数据不要超过n个 |
skip(n) | 跳过元素,返回一个跳过的n个元素的stream,若不足n个返回一个空流。 |
distinct() | 通过元素的hashcode和equals去除重复元素 |
//通过 filter方法 筛选大于15岁的人
Stream<ObJ> stream = list.stream();
stream.filter(obj-> obj.getAge()>15).limit(100).forEach(System.out::println);
// 通过list 得到前3个人
Stream<ObJ> stream = list.stream();
stream.filter(obj-> obj.getAge()>15).limit(3).forEach(System.out::println);
stream.limit(3).forEach(System.out::println);
通过这个可以看出创建的stream只能用一次。这的用一次的是不是说一个stream无法多次使用中间操作,而是使用了终止操作(forEach),就无法在进行操作了。
// 顺序
Stream<ObJ> stream = list.stream();
stream.limit(3).forEach(System.out::println);
System.out.println("-----------分割线-------");
// 并行
Stream<ObJ> stream1 = list.parallelStream();
stream1.limit(3).forEach(System.out::println);
可以看出并行,没有按照list添加的数据取出数据。
Stream<ObJ> stream = list.stream();
stream.skip(3).forEach(System.out::println);
Stream<ObJ> stream = list.stream();
stream.distinct().forEach(System.out::println);
映射
常用的方法如下:
方法 | 作用 |
map(Function f) | 将一个函数作为参数,该函数会被应用到每个元素上,并将其映射成要给新的元素 |
map ToDouble(ToDoubleFunction f) | 将函数作为要给参数,该函数会被应用到每一个元素上,产生一个新的DoubleStream。当然类似的还有:map ToLong(ToLongFunction f) ,mapToInt(ToIntFunction f) 等 |
flatMap(Function f) | 将一个函数作为参数,将流(stream)中的每个值都换成另一个流,然后把所有的流连接成一个流 |
如果懂点python的话感觉有点像是线程中调用map的方法。
// 判断输出名字里面含有王的名字。
Stream<ObJ> stream = list.stream();
stream.filter(t-> t.getName().contains("王")).limit(3).forEach(System.out::println);
System.out.println("-----------分割线-------");
stream = list.stream();
Stream<String> stream1= stream.map(t -> t.getName());
stream1.filter(t -> t.contains("王")).forEach(System.out::println);
public class test {
public static void main(String[] args) {
List<String> list=Arrays.asList("ab","cd","ef");
// 想要打印出 a b c d e f
// 如果使用map
Stream<String> stream=list.stream();
Stream<Stream<Character>> streamCs= stream.map(test::chai);
streamCs.forEach(streamC->
streamC.forEach(System.out::println)
);
System.out.println("-----------分割线-------");
// flatMap
stream=list.stream();
//Stream<Character> 这个直接将里面的集合进行展开
Stream<Character> streamC=stream.flatMap(test::chai);
streamC.forEach(System.out::println);
}
// 将字符串拆成一个字符
public static Stream<Character> chai(String str){
List list=new ArrayList<>();
for(Character c:str.toCharArray()) {
list.add(c);
}
// 直接返回一stream 为了好操作
return list.stream();
}
}
排序
方法 | 作用 |
sorted() | 会生成一个新的流,将其按自然顺序排序 |
sorted(Comparator com ) | 会生成一个新的流,将其按比较其顺序排序 |
public class test {
public static void main(String[] args) {
List<Integer> list=Arrays.asList(1,3,2);
Stream<Integer> stream=list.stream();
Stream<Integer> stream1=stream.sorted();
stream1.forEach(System.out::println);
System.out.println("-----------分割线-------");
stream=list.stream();
stream1=stream.sorted( (o1,o2) -> Integer.compare(o2, o1) );
stream1.forEach(System.out::println);
}
}
步骤3 终止操作
前面一种用的forEach就是常用的一个终止操作,当然还有一些其他的终止操作,具体如下。
匹配与查找
方法 | 作用 |
allMatch(Predicate p) | 检查是否匹配所有的元素,如果都满足返回true,哪怕一个不满足返回false |
anyMatch(Predicate p) | 检查是否至少匹配一个元素,如果有一个以及一个以上返回true,否则返回false |
noneMatch(Predicate p) | 检查是否没有匹配一个元素,哪怕一个满足返回false,否则返回true |
findFirst() | 返回最后一个元素,不过其返回的是一个Optional类 (这个后面补充) |
findAny() | 返回当前流中的任意元素,不过其返回的是一个Optional类 |
List<Integer> list=Arrays.asList(1,3,2);
Stream<Integer> stream=list.stream();
boolean b=stream.allMatch(t -> t>0);
System.out.println(b);
System.out.println("-----------分割线-------");
stream=list.stream();
b=stream.allMatch(t -> t>2);
System.out.println(b);
List<Integer> list=Arrays.asList(1,3,2);
Stream<Integer> stream=list.stream();
boolean b=stream.anyMatch(t -> t>0);
System.out.println(b);
System.out.println("-----------分割线-------");
stream=list.stream();
b=stream.anyMatch(t -> t>2);
System.out.println(b);
List<Integer> list=Arrays.asList(1,3,2);
Stream<Integer> stream=list.stream();
boolean b=stream.noneMatch(t -> t>2);
System.out.println(b);
System.out.println("-----------分割线-------");
stream=list.stream();
b=stream.noneMatch(t -> t>5);
System.out.println(b);
List<Integer> list=Arrays.asList(1,3,2);
Stream<Integer> stream=list.stream();
Optional<Integer> OP=stream.findFirst();//
System.out.println(OP.get());
System.out.println("-----------分割线-------");
stream=list.stream();
OP=stream.findAny();
System.out.println(OP.get());
归约
方法 | 作用 |
reduce(T identity, BinaryOperator<T> accumulator) | 可以将流中元素反复结合起来,得到一个值,返回T(interface BinaryOperator<T> extends BiFunction<T,T,T>) |
reduce( BinaryOperator<T> accumulator) | 可以将流中元素反复结合起来,得到一个值,返回 Optional<T> |
List<Integer> list=Arrays.asList(1,3,2);
// 把list中的值累加起来
Stream<Integer> stream=list.stream();
//为了方便理解 把这个初始值先写出来
Integer start=0;
Integer sum=stream.reduce(start, Integer::sum);
System.out.println(sum);
System.out.println("-----------分割线-------");
stream=list.stream();
start=1;
sum=stream.reduce(start, Integer::sum);
System.out.println(sum);
// 将 List<ObJ> list 中的年龄值相加
stream = list.stream();
Integer sumAge= stream.map(t -> t.getAge()).reduce(Integer::sum);
收集
方法 | 作用 |
collect(Collector C) | 将流转换为其他形式。接受一个Collector 接口的实现,用于给Stream中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集的操作,(如收集到List,Set,Map)另外还有很多静态方法,方便创建常见的收集器实例。
如下:
List<Integer> list=Arrays.asList(1,3,2);
// 把list中的值累加起来
Stream<Integer> stream=list.stream();
Stream<Integer> stream1 =stream.filter(t-> t>1);//
System.out.println(stream1);
System.out.println(stream1.collect(Collectors.toList())); //将 stream转换为一个list