Java8特性之Stream流

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a549654065/article/details/88932725

Java8特性之Stream流

一、Stream流概念


Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一 个则是 Stream API(java.util.stream.*)

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。


注意:

①Stream 自己不会存储元素。

②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

③Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行


二、Stream操作的三个步骤


1.创建Stream流

一个数据源(如:集合、数组),获取一个流。一般创建Stream流有以下几种方法

1)Collection 提供了两个方法 stream() 与 parallelStream()

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

2)通过 Arrays 中的 stream() 获取一个数组流

Integer[] nums = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(nums);

3)通过 Stream 类中静态方法 of()

Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);

4)创建无限流,也就是创建无尽的流

迭代

Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);

生成

Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);

2.中间操作

上面的代码只是创建了Stream流,并没有对集合数据有进行什么操作。接下来,我们可以对Stream流进行一系列流水线式的中间操作。多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值。

实际上,有很多种中间操作,筛选和切片、映射等等

2.1 筛选和切片

filter——接收 Lambda , 从流中排除某些元素。

limit——截断流,使其元素不超过给定数量。

skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补。

distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素。

示例:

	public class Employee {
       	private int id;
   		private String name;
   		private int age;
   		private double salary;
    	//...省略 getter/setter方法、toString、构造器方法
    }

   	List<Employee> employees = Arrays.asList(
    			new Employee("王五", 18, 9999.99), 
    			new Employee("李四", 38, 8888.88),
    			new Employee("张三", 50, 3333.33), 
    			new Employee("赵六", 16, 7777.77), 
    			new Employee("田七", 11, 6666.66));

	@Test
	public void test() {
		//中间操作不会执行任何操作,只有当你执行终止操作之后,一次性执行全部
		//也叫惰性求值 或 延时加载
		//中间操作
		Stream<Employee> stream = employees.stream().filter((e)->{
			System.out.println("Stream的中间操作");
			return e.getAge() > 35;
		});
		
		//终止操作
		stream.forEach(System.out::println);
	}

2.2 映射

map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
示例:

   	List<Employee> employees = Arrays.asList(
   			new Employee("王五", 18, 9999.99), 
   			new Employee("李四", 38, 8888.88),
   			new Employee("张三", 50, 3333.33), 
   			new Employee("赵六", 16, 7777.77), 
   			new Employee("田七", 11, 6666.66));

    	@Test
    	public void test() {
   			Stream<String> stream = employees.stream().map((e) -> e.getName());
   			stream.forEach(System.out::println);
    	}

输出:

   王五
   李四
   张三
   赵六
   田七

那么我们来写个例子,我们声明一个List如下

   List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");

现在有一个需求:我们需要将List中的String,拆分成每个字母

下面使用Map来实现

  public class TestStreamAPI{
   	@Test
   	public void test() {
   		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
   		Stream<Stream<Character>> stream = strList.stream().map(TestStreamAPI::filterCharacter);
   		stream.forEach((sm) -> {
   			sm.forEach(System.out::println);
   			});
   	}
   	public static Stream<Character> filterCharacter(String str){
   		List<Character> list = new ArrayList<>();
   		for (Character c: str.toCharArray()) {
   			list.add(c);
   		}
   		return list.stream();
   	}
  }

这样,中间操作生成的Stream类型居然是 Stream<Stream>,这样我们要得到每个字母就有点麻烦了

这个时候,flatMap出现了。


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

示例:

   public class Test{
       @Test
    	public void test() {
   		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
   		Stream<Character> stream = strList.stream().flatMap(Test::filterCharacter);
   		stream.forEach(System.out::println);
    	}
       
   	public static Stream<Character> filterCharacter(String str){
   		List<Character> list = new ArrayList<>();
   		for (Character c: str.toCharArray()) {
   			list.add(c);
   		}
   		return list.stream();
   	}
   }

输出:

   a
   a
   a
   b
   b
   b
   c
   c
   c
   d
   d
   d
   e
   e
   e

当然,flatMap和Map也是有区别的,以上的示例中,Map的操作是会把每个Stream流加入到自己的Stream中,也就是可以理解为一个Stream通过子Stream对集合进行操作,而flatMap是把集合的每一个元素添加到自己的Stream里,也就是Stream直接对集合进行操作


2.3 排序

sorted()——自然排序

示例:

	@Test
	public void test4() {
		List<String> strList = Arrays.asList("aaa", "eee", "ccc", "bbb", "ddd");
		strList.stream().sorted().forEach(System.out::println);
	}

输出:

aaa
bbb
ccc
ddd
eee

sorted(Comparator com)——定制排序

现在我们有个需求,将Employee先按照年龄排序,年龄相同按姓名排序

示例:

	public class Employee {
		private Integer id;
		private String name;
		private Integer age;
		private double salary;
    	//...省略 getter/setter方法、toString、构造器方法
	}

	List<Employee> employees = Arrays.asList(
			new Employee("王五", 18, 9999.99), 
			new Employee("李四", 38, 8888.88),
			new Employee("张三", 50, 3333.33), 
			new Employee("赵六", 16, 7777.77), 
			new Employee("田七", 11, 6666.66));

	@Test
	public void test() {
		employees.stream().sorted((e1,e2) -> {
			if (e1.getAge().equals(e2.getAge())) {
				return e1.getName().compareTo(e2.getName());
			}else {
				return e1.getAge().compareTo(e2.getAge());
			}
		}).forEach(System.out::println);
	}

输出:

Employee [id=null, name=田七, age=11, salary=6666.66]
Employee [id=null, name=赵六, age=16, salary=7777.77]
Employee [id=null, name=王五, age=18, salary=9999.99]
Employee [id=null, name=李四, age=38, salary=8888.88]
Employee [id=null, name=张三, age=50, salary=3333.33]

3.终止操作

一个终止操作,执行中间操作链,并产生结果

有很多种终止操作,下面我演示一下常用的。


3.1 查找与匹配

新建一个Employee类

	public class Employee {

		private Integer id;
		private String name;
		private Integer age;
		private double salary;
		private Status Status;
		public enum Status{
			FREE,
			BUSY,
			VOCATION;
		}
    	//...省略 getter/setter、toString、构造器方法
	}

我们在测试类新添加一个属性

	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66, Status.BUSY),
			new Employee(101, "张三", 18, 9999.99, Status.FREE),
			new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
			new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
			new Employee(104, "刘八", 8, 7777.77, Status.FREE),
			new Employee(104, "陈九", 8, 7777.77, Status.FREE),
			new Employee(105, "田七", 38, 5555.55, Status.BUSY)
	);

开始测试

1)allMatch——检查是否匹配所有元素

2)anyMatch——检查是否至少匹配一个元素

3)noneMatch——检查是否没有匹配的元

// allMatch
Boolean b1 = emps.stream().allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(b1);
// anyMatch		
Boolean b2 = emps.stream().anyMatch((e)->e.getStatus().equals(Status.BUSY));
System.out.println(b2);
// noneMatch
Boolean b3 = emps.stream().noneMatch((e)->e.getStatus().equals(Status.BUSY));
System.out.println(b3);

输出 :

false
true
false

4)findFirst——返回第一个元素

5)findAny——返回当前流中的任意元素

Optional<Employee> op = emps.stream()
    .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
		.findFirst();
System.out.println(op.get());

Optional<Employee> op2 = emps.parallelStream()
    .filter((e) -> e.getStatus().equals(Status.FREE)).findAny();
System.out.println(op2.get());

输出

Employee [id=103, name=王五, age=28, salary=3333.33, Status=VOCATION]
Employee [id=101, name=张三, age=18, salary=9999.99, Status=FREE]

这里我要稍微讲一下,stream()和parallelStream()的区别,parallelStream是同时有多个线程运行,而stream是串行的,每次运行只有一个线程。所以上面的输出,第二行parallelStream流同时多个线程开始findAny(),谁先找到就是谁的,那如果将parallelStream()改成stream()的话,第二行的输出总是会固定的


6)count——返回流中元素的总个数

7)max——返回流中最大值

8)min——返回流中最小值

System.out.println(emps.stream().count());
		
Optional<Employee> op3 = emps.stream()
		.max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op3.get());
		
Optional<Employee> op4 = emps.stream()
	.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op4.get());

输出

7
Employee [id=101, name=张三, age=18, salary=9999.99, Status=FREE]
Employee [id=103, name=王五, age=28, salary=3333.33, Status=VOCATION]

3.2 归约与收集

归约——可以将流中元素反复结合起来,得到一个值。返回 T

1)归约 reduce(T identity, BinaryOperator)


现有个需要,计算一个整型数组里的总和

平时我们会这样写

int[] arr = {1,2,3,4,5,6,7,8}
for (int i = 0; i < arr.length; i++)
    sum += arr[i];

那有了流之后呢?我们可以利用归约来实现

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
// 首先有个起始值0,作为x,再向流中取出值1 作为y | x + y = 1
// 再将1 作为x,再想流取出 值2
int sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);

输出:

28

2)归约 reduce(BinaryOperator)

示例:

public class Employee {
    
	private Integer id;
	private String name;
	private Integer age;
	private double salary;
	//...省略 getter/setter方法、toString、构造器方法
}

List<Employee> emps = Arrays.asList(
    new Employee(102, "李四", 59, 6666.66),
	new Employee(101, "张三", 18, 9999.99), 
    new Employee(103, "王五", 28, 3333.33),
	new Employee(104, "赵六", 8, 7777.77), 
    new Employee(104, "刘八", 8, 7777.77),
	new Employee(104, "陈九", 8, 7777.77),
    new Employee(105, "田七", 38, 5555.55));

@Test
public void test() {
	// 可能为空的值封装到Optional
	Optional<Double> op = emps.stream().map(Employee::getSalary).reduce(Double::sum);
	System.out.println(op.get());
}

输出:

48888.84000000001

收集——将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法

收集 collect(Collector c)

示例:将转化为List、Set、HashSet类型

public class Employee {
    
	private Integer id;
	private String name;
	private Integer age;
	private double salary;
	//...省略 getter/setter方法、toString、构造器方法
}

List<Employee> emps = Arrays.asList(
    new Employee(102, "李四", 59, 6666.66),
	new Employee(101, "张三", 18, 9999.99), 
    new Employee(103, "王五", 28, 3333.33),
	new Employee(104, "赵六", 8, 7777.77), 
    new Employee(104, "刘八", 8, 7777.77),
	new Employee(104, "陈九", 8, 7777.77),
    new Employee(105, "田七", 38, 5555.55));
	
@Test
public void test3() {
	List<String> list =emps.stream().map(Employee::getName).collect(Collectors.toList());
	list.forEach(System.out::println);
	System.out.println("--------------------------------");
	Set<String> set = emps.stream().map(Employee::getName).collect(Collectors.toSet());
	set.forEach(System.out::println);
	System.out.println("--------------------------------");
	HashSet<String> hs = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
	hs.forEach(System.out::println);
}

输出:

李四
张三
王五
赵六
刘八
陈九
田七
--------------------------------
李四
张三
王五
陈九
赵六
刘八
田七
--------------------------------
李四
张三
王五
陈九
赵六
刘八
田七

猜你喜欢

转载自blog.csdn.net/a549654065/article/details/88932725