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);
	
	}
}

java通过stream设置对象的值 stream java_java通过stream设置对象的值

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);
	
	}
}

java通过stream设置对象的值 stream java_java通过stream设置对象的值_02

步骤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);

java通过stream设置对象的值 stream java_java通过stream设置对象的值_03

// 通过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);

java通过stream设置对象的值 stream java_1.8_04

通过这个可以看出创建的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);

java通过stream设置对象的值 stream java_System_05

可以看出并行,没有按照list添加的数据取出数据。

Stream<ObJ> stream = list.stream();
      stream.skip(3).forEach(System.out::println);

java通过stream设置对象的值 stream java_1.8_06

Stream<ObJ> stream = list.stream();
      stream.distinct().forEach(System.out::println);

java通过stream设置对象的值 stream java_stream_07

映射

常用的方法如下:

方法

作用

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);

java通过stream设置对象的值 stream java_java通过stream设置对象的值_08

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);
	}  
}

java通过stream设置对象的值 stream java_1.8_09

步骤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);

java通过stream设置对象的值 stream java_java_10

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);

java通过stream设置对象的值 stream java_System_11

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);

java通过stream设置对象的值 stream java_stream_12

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);

java通过stream设置对象的值 stream java_stream_13

// 将 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)另外还有很多静态方法,方便创建常见的收集器实例。

如下:

java通过stream设置对象的值 stream java_java_14

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

java通过stream设置对象的值 stream java_System_15