java8 stream(完整例子)

java8 stream


  学习 Stream 之前必须先学习 lambda 的相关知识,学习lambda又涉及到匿名内部类,所以这里先介绍匿名内部类,在介绍lambda表达式,最后介绍java8 新特性 Stream

1.匿名内部类

  new 实现接口() |父类构造器(实参列表)
  {
  //匿名内部类的类体部分
  }

1.1 父类构造器方式实现匿名内部类

public class A 
{
    public A() {
		super();
	}

	public A(String name) {
		super();
		this.name = name;
	}

	private String name;

	public String getName() 
	{
		return name;
	}

	public void setName(String name) 
	{
		this.name = name;
	}
}

public class B extends A
{
	public String getName() 
	{
		return "B";
	}
}
public class Test2 
{
	public void print(A a)
	{
		System.out.println(a.getName());
	}
    public static void main(String[] args) 
    {
    	//1.
		Test2 test = new Test2();
		test.print(new A() 
		{
			public String getName()
			{
				return "ttt";
				
			}
		});
		//2. 
		test.print(new B());
	}
}

   对比main 方法里面的1处和2 处,显然1处匿名内部类写法更加简洁,而2处还要去定义一个子类B

1.2 实现接口方式实现匿名内部类

public interface Product 
{
   public String getName();
}
public interface Product 
{
   public String getName();
}
public class Iphone implements Product{

	@Override
	public String getName() 
	{
		return "iphone手机";
	}

}
public class Test 
{
	public void print(Product p)
	{
		System.out.println(p.getName());
	}
    public static void main(String[] args) 
    {
    	//1.
		Test test = new Test();
		test.print(new Product() 
		{
			public String getName()
			{
				return "huawei手机";
			}
		});
		//2.
		test.print(new Iphone());
		
	}
}

   对比main 方法里面的1处和2 处,显然1处匿名内部类写法更加简洁,而2处还要去定义一个实现类Iphone

2.lambda表达式

2.1 lambad表达式和匿名内部类之间的联系

   lambad表达式 和匿名内部类有什么联系?这里我们先通过一个例子来看下:

public interface Calculable 
{
	   int calculateInt(int a, int b);
}
public class Test 
{
	
	public int add (int a , int b)
	{
		Calculable c = new Calculable()
		{
			@Override
			public int calculateInt(int a, int b) {
				// TODO Auto-generated method stub
				return a+b;
			}
		};
		return c.calculateInt(a, b);
		
	}
	
	public int sub (int a, int b)
	{
		Calculable c = new Calculable()
		{
			@Override
			public int calculateInt(int a, int b) {
				// TODO Auto-generated method stub
				return a-b;
			}
		};
		return c.calculateInt(a, b);
	}
    public static void main(String[] args) 
    {
		Test test = new Test();
		int result = test.add(3, 4);
		int result2 = test.sub(13, 4);
		System.out.println(result);
		System.out.println(result2);
	}
}

  上述代码以匿名内部类的形式实现加法,减法运算

public class Test2 
{
	
	public int add (int x , int y)
	{
		Calculable c = (int a,int b) -> {return a+b;};
		return c.calculateInt(x, y);
	}
	
	public int sub (int x, int y)
	{
		Calculable c = (int a , int b) -> {return a-b;};
		return c.calculateInt(x, y);
	}
    public static void main(String[] args) 
    {
		Test2 test = new Test2();
		int result = test.add(3, 4);
		int result2 = test.sub(13, 4);
		System.out.println(result);
		System.out.println(result2);
	}
}

   用 Lambda 表达式替代匿名内部类,可见代码变得简洁。通过以上示例我们发现,Lambda 表达式是实现接口的匿名内部类对象,可以作为表达式、方法参数和方法返回值。

   如果我们在 Calculable 接口在增加一个抽象方法如下:

在这里插入图片描述
  会出现如下报错:
在这里插入图片描述
   因为Lambda 表达式实现的接口不是普通的接口,而是函数式接口。所以这种接口只能有一个方法。如果接口中声明多个抽象方法,那么 Lambda 表达式会发生编译错误:
   The target type of this expression must be a functional interface

   函数式接口就是只显式声明一个抽象方法的接口。为保证方法数量不多不少,java8提供了一个专用注解@FunctionalInterface,这样,当接口中声明的抽象方法多于或少于一个时就会报错。如下图所示:
在这里插入图片描述                图一
在这里插入图片描述                图二

2.2 lambad表达式语法

   lambad表达式主要主要就是替代匿名内部的繁琐语法,它由3部分组成:
   1.形参类别。 允许省略形参类型
   2.箭头( ->)。
   3.代码块。只有一条语句,可以省略花括号,只有一条return语句,可以省略return关键字,lambda会自动返回这条语句的值。

    (int x, int y) -> x + y;

参数类型也可以省略,Java编译器会根据上下文推断出来:

    (x, y) -> x + y; //返回两数之和
 
或者

    (x, y) -> { return x + y; } 

在这里插入图片描述

3.java8 stream

   Stream有如下三个操作步骤:

   一、创建Stream

   从一个数据源,如集合、数组中获取流。

   二、中间操作

   一个操作的中间链,对数据源的数据进行操作。

   三、终止操作

   一个终止操作,执行中间操作链,并产生结果。
在这里插入图片描述
   无状态:指元素的处理不受之前元素的影响;

   有状态:指该操作只有拿到所有元素之后才能继续下去。

   非短路操作:指必须处理所有元素才能得到最终结果;

  短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

3.1 创建Stream

3.1.1 Arrays.stream()

   当在日常编程中面对的是一个数组,也可以使用Arrays.stream()方法来使用Stream

	    Integer [] intArrays = new Integer [] {1,2,3};
    	Stream<Integer> intArrayStream = Arrays.stream(intArrays);
    	intArrayStream.forEach(e->System.out.println(e));
3.1.2 Stream.of()

   当面对数组时除了可以使用Arrays.stream()方法外,还可以使用Stream将需要的数组转成Stream。这个方法不但支持传入数组,将数组转成Stream,也支持传入多个参数,将参数最终转成Stream

Integer [] intArrays = new Integer [] {1,2,3};
Stream<Integer> intStream = Stream.of(1,2,3,4);
Stream<Integer> intStream2 = Stream.of(intArrays);
3.1.3 Stream.generate()

  Stream接口有两个用来创建无限Stream的静态方法。一个是generate()方法接受一个参数函数,可以使用类似如下代码来创建一个你需要的Stream。

		Stream<String> generateStream = Stream.generate(()->{return "test";}).limit(5);
		generateStream.forEach(e->System.out.println(e));
3.1.4 Stream.iterate()

  stream接口的另一个用来创建无限Stream的静态方法就是iterate()方法。iterate()方法也是接受一个参数函数,可以用类似如下代码来创建一个你需要的Stream。

		Stream<Integer> iterateStream = Stream.iterate(10, a->a+2).limit(6);
		iterateStream.forEach(e->System.out.println(e));
3.1.5 使用Collection下的 stream() 和 parallelStream() 方法

   Stream可以分为串行与并行两种,串行流和并行流差别就是单线程和多线程的执行。
   default Stream stream() : 返回串行流
   default Stream parallelStream() : 返回并行流

	    List<String> list = new ArrayList<String>();
		list.add("a");
		list.add("b");
		list.add("c");
		Stream<String> stream = list.stream();
		Stream<String> parallelStream = list.parallelStream();
		stream.forEach(e->System.out.println(e));
		parallelStream.forEach(e->System.out.println(e));

3.2 创建Stream

3.2.1 筛选与切片

   filter:Stream的过滤转换,此方法会生成一个新的流,其中包含符合某个特定条件的所有元素。

	   Integer [] intArrays = new Integer [] {1,2,3};
   	   Stream<Integer> intArrayStream = Arrays.stream(intArrays).filter(e->e>2);
   	   intArrayStream.forEach(e->System.out.println(e));

   limit(n):该方法会返回一个包含n个元素的新的流(若总长小于n则返回原始流)

	   String [] stringArrays = new String [] {"a","b","c","d","e"};
	   Stream<String> stringStream = Arrays.stream(stringArrays).limit(2);
	   stringStream.forEach(e->System.out.println(e));

   skip(n):方法与limit(n)正好相反,它会丢弃掉前面的n个元素。

       String [] stringArrays = new String [] {"a","b","c","d","e"};
	   Stream<String> skipStream = Arrays.stream(stringArrays).skip(2);
	   skipStream.forEach(e->System.out.println(e));

   distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

       Integer [] intArrays = new Integer [] {1,2,3,3,2,1};
       Stream<Integer> distinctStream = Arrays.stream(intArrays).distinct();
	   distinctStream.forEach(e->System.out.println(e));
3.2.2 映射

   map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

   flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

       List<String> stringList = Arrays.asList("a,b,c", "1,2,3");
	   Stream<String> mapStream = stringList.stream().map(e->e.replace(",", ""));
	   mapStream.forEach(e->System.out.println(e));//abc 123
	   Stream<String> flatMapStream = stringList.stream().flatMap((e)->{String [] arrs =    e.split(",");return Arrays.stream(arrs);});
	   flatMapStream.forEach(e->System.out.println(e));//a b c 1 2 3
3.2.3 排序

  sorted():自然排序,流中元素需实现Comparable接口
  sorted(Comparator com):定制排序,自定义Comparator排序器

public class Product implements Comparable
{
    private double price;


	public Product() {
		super();
	}


	public Product(double price) {
		super();
		this.price = price;
	}


	@Override
	public int compareTo(Object o) 
	{
		Product other = (Product)o;
		//由低到高排序
		if (this.price > other.price)
		{
			return 1;
		}
		else if (this.price == other.price)
		{
			return 0;
		}
		else
		{
			return -1;
		}
	}


	@Override
	public String toString() {
		return "Product [price=" + price + "]";
	}


	public double getPrice() {
		return price;
	}


	public void setPrice(double price) {
		this.price = price;
	}
	
	
}
	    Product a = new Product(3.14);
		Product b = new Product(5.67);
		Product c = new Product(2.11);
		List<Product> productList = Arrays.asList(a, b, c);
		Stream <Product> productStream = productList.stream().sorted();
		productStream.forEach(e->System.out.println(e));// Product [price=2.11] Product [price=3.14] Product [price=5.67]
		Stream <Product> comparatorStream = productList.stream().sorted((o1,o2)->{
			Product p1 = (Product)o1;
			Product p2 = (Product)o2;
			if (p1.getPrice() > p2.getPrice())
			{
				return 1;
			}
			else if (p1.getPrice() == p2.getPrice())
			{
				return 0;
			}
			else
			{
				return -1;
			}
		});
		comparatorStream.forEach(e->System.out.println(e)); // Product [price=2.11] Product [price=3.14] Product [price=5.67]
3.2.4 消费

  peek:如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。

        Product a = new Product(3.14);
		Product b = new Product(5.67);
		Product c = new Product(2.11);
		List<Product> productList = Arrays.asList(a, b, c);
		Stream <Product> peekStream =  productList.stream().peek(e->e.setPrice(1.1));
		peekStream.forEach(e->System.out.println(e));//Product [price=1.1] Product [price=1.1] Product [price=1.1]

3.3 流的终止操作

3.3.1 匹配、聚合操作

  allMatch:接收一个 Predicate 函数,当流中所有元素都符合该断言时才返回true,否则返回false
  noneMatch:接收一个 Predicate 函数,当流中所有元素都不符合该断言时才返回true,否则返回false
  anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
  findFirst:返回流中第一个元素
  findAny:返回流中的任意元素
  count:返回流中元素的总个数
  max:返回流中元素最大值
  min:返回流中元素最小值

         Integer x = Arrays.stream(intArrays).findFirst().get();
		 Integer y = Arrays.stream(intArrays).findAny().get();
		 long count = Arrays.stream(intArrays).count();
		 Integer maxInteger = Arrays.stream(intArrays).max((o1,o2)->{return o1.compareTo(o2);}).get();
		 Integer minInteger = Arrays.stream(intArrays).min( Integer::compareTo).get();
		 System.out.println(x);//1
		 System.out.println(y);//1
		 System.out.println(count);//6
		 System.out.println(maxInteger);//3
		 System.out.println(minInteger);//1

	   List<Integer> intList3 = Arrays.asList(4,1,10,100,2,5,19,3,3);
	   Boolean b = intList3.stream().allMatch(e->e >= 1);
	   System.out.println(b);//true
	   Boolean b2 = intList3.stream().allMatch(e->e >= 2);
	   System.out.println(b2);//false
	   Boolean b3 = intList3.stream().anyMatch(e->e >= 1);
	   System.out.println(b3);//true
	   Boolean b4 = intList3.stream().anyMatch(e->e >= 101);
	   System.out.println(b4);//false
	   Boolean b5 = intList3.stream().noneMatch(e->e >= 1);
	   System.out.println(b5);//false
	   Boolean b6 = intList3.stream().noneMatch(e->e >= 101);
	   System.out.println(b6);//true
3.3.2 规约操作操作

  Optional reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。
  T reduce(T identity, BinaryOperator accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
   U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator combiner):在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。在并行流(parallelStream)中,我们知道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。

		 List<Integer> list = Arrays.asList(1,2,3,4,5);
		 Integer reduce = list.stream().reduce((x1,x2)->{return x1+x2;}).get();
		 System.out.println(reduce);//15
		 Integer reduce2 = list.stream().reduce(3, (x1,x2)->{return x1+x2;});
		 System.out.println(reduce2);//18
		 Integer reduce3 = list.stream().reduce(3, (x1,x2)->{return x1+x2;}, (x1,x2)->{return x1*x2;});
		 System.out.println(reduce3);//18
		 Integer reduce4 = list.parallelStream().reduce(3, (x1,x2)->{return x1*x2;});
		 System.out.println(reduce4);//29160

3.3.3 收集操作
3.3.3.1 收集到集合
       //收集到一个List
	   List<Integer> list = Arrays.asList(4,1,10,100,2,5,19,3,3);
	   List<Integer> newList = list.stream().collect(Collectors.toList());
	   System.out.println(newList);//[4, 1, 10, 100, 2, 5, 19, 3, 3]
	
	   //收集到一个set
	   Set<Integer> sets = list.stream().collect(Collectors.toSet());
	   System.out.println(sets );//[1, 2, 19, 3, 4, 100, 5, 10]
	   //如果想控制返回set的类型
	   TreeSet<Integer> sets2 = list.stream().collect(Collectors.toCollection(TreeSet::new));
	   System.out.println(sets2);//[1, 2, 3, 4, 5, 10, 19, 100]
	   
	   
	   //收集到一个map
	   List<Room> lsitRoom = Arrays.asList( new Room(2,1),new Room(17,1), new Room(10,1),new Room(1,3));
	   List<Room> lsitRoom2 = Arrays.asList( new Room(2,1),new Room(17,1), new Room(10,1),new Room(1,3), new Room(1,2));
       //当我们希望将集合中的元素收集到Map中时,可以使用Collectors.toMap方法。这个方法有两个参数,用来生成Map的key和value。
	   Map<Integer,Integer> maps = lsitRoom.stream().collect(Collectors.toMap(Room::getHeigh, Room::getWidth));
	   System.out.println(maps);//{1=2, 17=1, 2=1, 10=1}
	   //如果多个元素拥有相同的键,在收集结果时会抛出java.lang.IllegalStateException异常。可以使用第三个参数来解决,第三个参数用来确定当出现键冲突时,该如何处理结果,如果当出现键冲突时只保留一个并且是保留已经存在的值时,就是如下方式。
	   Map<Integer,Integer> maps2 = lsitRoom2.stream().collect(Collectors.toMap(Room::getHeigh, Room::getWidth, (nowkey,newKey) -> nowkey));
	   System.out.println(maps2);//{1=2, 17=1, 2=1, 10=1}
	   //但是通常还是以具体元素作为值的情况多,可以使用Function.identity()来获取实际元素。
	   Map<Integer,Room> roomMaps1 = lsitRoom.stream().collect(Collectors.toMap(Room::getHeigh, Function.identity()));
	   System.out.println(roomMaps1);//{1=Room [heigh=1, width=3], 17=Room [heigh=17, width=1], 2=Room [heigh=2, width=1], 10=Room [heigh=10, width=1]}
	   //如果想指定生成的Map类型,则还需要第三个参数
	   TreeMap<Integer,Room> roomMaps2 = lsitRoom.stream().collect(Collectors.toMap(Room::getHeigh, Function.identity(), (nowkey,newKey) -> nowkey,TreeMap::new));
	   System.out.println(roomMaps2);//{1=Room [heigh=1, width=3], 2=Room [heigh=2, width=1], 10=Room [heigh=10, width=1], 17=Room [heigh=17, width=1]}
3.3.3.2 拼接字符串
       List<Integer> intList = Arrays.asList(4,1,10,100,2,5,19,3,3);
	   String s = intList.stream().map(String::valueOf).collect(Collectors.joining(","));
	   System.out.println(s);
3.3.3.2 收集聚合

  分别收集流的总和、平均值、最大值或者最小值

	   List<Integer> intList2 = Arrays.asList(4,1,10,100,2,5,19,3,3);
	   Double x = intList2.stream().collect(Collectors.averagingInt(Integer::intValue));
	   System.out.println(x);
	   int sum = intList2.stream().collect(Collectors.summingInt(Integer::intValue));
	   System.out.println(sum);
	   int max = intList2.stream().collect(Collectors.maxBy(Integer::compare)).get();
	   System.out.println(max);
3.3.3.3 分组

  在一个集合中,对具有相同特性的值进行分组是一个很常见的功能

       //分组
	   List<Room> newListRoom = Arrays.asList( new Room(2,1),new Room(2,3), new Room(17,1), new Room(17,3), new Room(10,1),new Room(1,3));
	   //按高度分组
	   Map<Integer,List<Room>> mapRoom = newListRoom.stream().collect(Collectors.groupingBy(Room::getHeigh));
       System.out.println(mapRoom);//{1=[Room [heigh=1, width=3]], 17=[Room [heigh=17, width=1], Room [heigh=17, width=3]], 2=[Room [heigh=2, width=1], Room [heigh=2, width=3]], 10=[Room [heigh=10, width=1]]}
       //多级分组
       //第一级我们将房间按照高度分组,第二级按照宽度分组。
       List<Room> newListRoom2 = Arrays.asList( new Room(2,1),new Room(2,3), new Room(17,1), new Room(17,3), new Room(17,4), new Room(17,3), new Room(10,1),new Room(1,3));
       Map<Integer,Map<Integer,List<Room>>> mapsss = newListRoom2.stream().collect(Collectors.groupingBy(Room::getHeigh,Collectors.groupingBy(Room::getWidth)));
       System.out.println(JSON.toJSONString(mapsss));
       /*
        
           {
				1: {
					3: [{
						"heigh": 1,
						"width": 3
					}]
				},
				17: {
					1: [{
						"heigh": 17,
						"width": 1
					}],
					3: [{
						"heigh": 17,
						"width": 3
					}, {
						"heigh": 17,
						"width": 3
					}],
					4: [{
						"heigh": 17,
						"width": 4
					}]
				},
				2: {
					1: [{
						"heigh": 2,
						"width": 1
					}],
					3: [{
						"heigh": 2,
						"width": 3
					}]
				},
				10: {
					1: [{
						"heigh": 10,
						"width": 1
					}]
				}
			}
        
        */

3.3.3.4 分片

  当分类函数是一个返回布尔值的函数时,流元素会被分为两组列表:一组是返回true的元素集合,另一组是返回false的元素集合。这种情况适用partitoningBy方法会比groupingBy更有效率

       //分片
	   Map<Boolean,List<Room>> mapRoom2 = newListRoom.stream().collect(Collectors.partitioningBy(e->e.getHeigh() > 10));
       System.out.println(mapRoom2);//{false=[Room [heigh=2, width=1], Room [heigh=2, width=3], Room [heigh=10, width=1], Room [heigh=1, width=3]], true=[Room [heigh=17, width=1], Room [heigh=17, width=3]]}

3.3.3.5 扩展功能

  counting方法会返回收集元素的总个数


       //counting
       Long longx = newListRoom.stream().filter(e->e.getHeigh() > 2).collect(Collectors.counting());
       System.out.println(longx);//3
       Map<Integer,Long> mapRoomN = newListRoom.stream().collect(Collectors.groupingBy(Room::getHeigh,Collectors.counting()));
       System.out.println(mapRoomN);//{1=1, 17=2, 2=2, 10=1}

  mapping方法会将结果应用到另一个收集器上

       //mapping 
       //按高度分组 取出房间里最大的宽度
       Map<Integer, Optional<Integer>> mapt = newListRoom.stream().collect(Collectors.groupingBy(Room::getHeigh,Collectors.mapping(Room::getWidth, Collectors.maxBy(Integer::compare))));
       System.out.println(mapt);//{1=Optional[3], 17=Optional[3], 2=Optional[3], 10=Optional[1]}
发布了56 篇原创文章 · 获赞 1 · 访问量 1168

猜你喜欢

转载自blog.csdn.net/atu1111/article/details/104695136
今日推荐