Stream——Java

目录

一、流的概述

二、流的创建

三、流的转换 

四、Optional类型

五、流的计算结果

六、流的应用


一、流的概述

定义:

 特点:

pipelining:很多流的操作也是返回一个流

Internal Iteration:流操作自动进行迭代,用户感知不到循环遍历。

工作流程:

  • 流的创建
  • 流转换为其他流的中间操作,可以包括多个步骤(惰性步骤)
  • 流的计算结果。这个操作会强制执行之前的惰性操作。这个步骤以后,流就再也不能用了

 示例:将grocery订单按金额从大到小输出id.

import java.util.*;
import java.util.stream.Collectors;

public class StreamDemo {

	public static void main(String[] args) {
		List<Transaction> transactions = new ArrayList<Transaction>();
		transactions.add(new Transaction(1, 100, "batch"));
		transactions.add(new Transaction(3, 80, "grocery"));
		transactions.add(new Transaction(6, 120, "grocery"));
		transactions.add(new Transaction(7, 40, "batch"));
		transactions.add(new Transaction(10, 50, "grocery"));

		// 采用传统方法
		traditionalMethod(transactions);
		System.out.print("============\n");
		//流方法
		streamMethod(transactions);
	}

	public static void traditionalMethod(List<Transaction> transactions) {
		// 过滤保留type = "grocery"的记录
		List<Transaction> groceryTransactions = new ArrayList<>();
		for (Transaction t : transactions) {
			if (t.getType().equals("grocery")) {
				groceryTransactions.add(t);
			}
		}

		// 根据value对符合的记录排序,从高到低
//		Collections.sort(groceryTransactions, new Comparator<Transaction>() {
//			public int compare(Transaction t1, Transaction t2) {
//				return t2.getValue().compareTo(t1.getValue());
//			}
//		});
		//利用Lambda表达式
		Collections.sort(groceryTransactions, (t1,t2) ->t2.getValue().compareTo(t1.getValue()));		

		// 获取记录中的id字段
		List<Integer> transactionIds = new ArrayList<>();
		for (Transaction t : groceryTransactions) {
			transactionIds.add(t.getId());
		}

		// 输出结果
		transactionIds.forEach(System.out::println);
	}
	//流技术的运用
	public static void streamMethod(List<Transaction> transactions) {
		transactions.stream().filter(t->t.getType().equals("grocery"))
		  .sorted(Comparator.comparing(Transaction::getValue).reversed())
		  .map(Transaction::getId)
		  .collect(Collectors.toList())
		  .forEach(System.out::println);
	}
}

class Transaction {
	int id;
	Integer value;
	String type;

	public Transaction(int id, int value, String type) {
		super();
		this.id = id;
		this.value = value;
		this.type = type;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Integer getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

}
输出:
6
3
10
============
6
3
10

二、流的创建

1.Collection接口的stream方法,将其转换

		// Collection子类产生stream
		Stream<String> a1 = new ArrayList<String>().stream();
		Stream<String> a2 = new HashSet<String>().stream();
		// 使用Arrays.stream 转化数组为stream
		Stream<String> b1 = Arrays.stream("a,b,c,d,e".split(","), 3, 5);

2.用Stream类进行转化:

  • of方法将数组转化,
  • empty方法产生一个空流,
  • generate方法接收Lambda表达式,
  • iterate方法接收一个种子和Lambda表达式。
		// 数组产生stream
		Stream<Integer> c1 = Stream.of(new Integer[5]);
		Stream<String> c2 = Stream.of("a,b,c".split(","));
		Stream<String> c3 = Stream.of("a", "b", "c");
		
		//空流
		Stream<String> d1 = Stream.empty();
		
		//无限流,使用generate方法,根据Lambda表达式产生
		Stream<String> e1 = Stream.generate(()->"hello");
		Stream<Double> e2 = Stream.generate(Math::random);
		
		//无限流,使用iterate方法,第一个参数是种子
		//第二个是Lambda表达式
		Stream<BigInteger> e3 = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
	

其他创建流的方法	
		
//Files的lines方法读取一个文件,产生每一行内容的Stream
		Stream<String> contents = Files.lines(Paths.get("C:/abc.txt")); 
		
		//Pattern的splitAsStream方法,根据一个正则表达式,将内容分为一个字符串的Stream
		Stream<String> words = Pattern.compile(",").splitAsStream("a,b,c");
		

3.基本类型流

只有IntStream,LongStream,DoubleStream

		IntStream s1 = IntStream.of(1,2,3,4,5);
		
		s1 = Arrays.stream(new int[] {1,2,3});		
		s1 = IntStream.generate(()->(int)(Math.random() * 100));		
		s1 = IntStream.range(1,5); //1,2,3,4  step 1		
		s1 = IntStream.rangeClosed(1,5); //1,2,3,4,5
		
	

4.基本类型流和对象流的转换

		IntStream s2 = IntStream.of(1,2,3,4,5);
        Stream<Integer> s3 = s2.boxed();//转换为对象流
		IntStream s5 = s3.mapToInt(Integer::intValue);//对象流转换为基本流

5.并行流的创建

三、流的转换 

有这么几类操作:过滤、去重、排序、转化、抽取/跳过/连接、其他

1.过滤:

public class StreamFilter {

	public static void main(String[] args) {
		System.out.println("======对每个元素进行过滤判定================");
		
		Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 5);
		Stream<Integer> s2 = s1.filter(n -> n>2);
		s2.forEach(System.out::println);
		//3, 4, 5
	}
}

2.去重distinct();

对流元素进行过滤,去除重复,只留下不重复的。

当去重对象时,会调用hashCoode再调用equals方法进行判重

import java.util.ArrayList;
import java.util.stream.Stream;

public class StreamDistinct {

	public static void main(String[] args) {
		System.out.println("======基本类型包装类对象去重================");
		
		Stream<Integer> s1 = Stream.of(1, 1, 2, 2, 3, 3);
		Stream<Integer> s2 = s1.distinct();
		s2.forEach(System.out::println);
		// 1, 2, 3
		
		System.out.println("======自定义对象的去重================");

		ArrayList<Student> students = new ArrayList<Student>();
		students.add(new Student("Tom", 20));
		students.add(new Student("Tom", 20));
		students.add(new Student("Jerry", 20));
		students.add(new Student("Jerry", 18));

		// 先对象的hashCode再调用equals方法进行判重
		Stream<Student> s3 = students.stream().distinct();
		s3.forEach(System.out::println);

	}
}

class Student {
	private String name;
	private int age;

	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public int hashCode() {
		return name.hashCode() * 1000 + age;
	}
	
	@Override
	public boolean equals(Object o) {
		Student s = (Student) o;
		if ((this.age == s.age) 
				&& this.name.equals(s.name)) {
			return true;
		} else {
			return false;
		}
	}

	
	public String toString() {
		return "name:" + name + ", age:" + age;
	}

}

3.排序

sort()方法:对流的基本类型包装类元素进行排序

可提供Comparator,对流进行排序

可在流的自定义对象进行排序,对象内实现compareTo方法

import java.util.ArrayList;
import java.util.Comparator;
import java.util.stream.Stream;

public class StreamOrder {
	public static void main(String[] args) {
		System.out.println("=======对基本类型包装类对象进行排序======");
		
		Stream<Integer> s1 = Stream.of(3,2,4,1,5);
		Stream<Integer> s2 = s1.sorted();
		s2.forEach(System.out::println);
		//1, 2, 3, 4, 5
		
		System.out.println("=======提供Comparator进行排序===============");
		
		
		String[] planets = new String[] { 
				"Mercury", "Venus", "Earth", 
				"Mars", "Jupiter", "Saturn", 
				"Uranus", "Neptune" };
		
		Stream<String> s3 = Stream.of(planets).sorted(
				Comparator.comparing(String::length));
		s3.forEach(System.out::println);
		
		System.out.println("========对自定义对象进行排序==============");
		
		ArrayList<Cat> cats = new ArrayList<>();
		cats.add(new Cat(3));
		cats.add(new Cat(2));
		cats.add(new Cat(5));
		cats.add(new Cat(1));
		cats.add(new Cat(4));
		Stream<Cat> s4 = cats.stream().sorted();
		s4.forEach(System.out::println);
	}	
}

class Cat implements Comparable<Cat> {
	private int size;

	public Cat(int size) {
		super();
		this.size = size;
	}

	@Override
	public int compareTo(Cat o) {
		Cat c = new Cat(5);
		System.out.println(c.size);
		return this.size - o.size;	
	}
	
	public String toString()
	{
		return "Size:" + size;
	}
}

4.转化map:

  • 利用方法引用,对流的每一个元素进行函数计算。
  • 利用Lambda表达式,对流的每一个元素进行计算。
  • 利用方法引用,对流的每一个元素进行函数计算返回Stream
  • 利用方法引用,对流的每一个元素进行函数计算返回Stream,并合并所有结果
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamMap {

	public static void main(String[] args) {
		System.out.println("======用方法引用对每个元素进行计算=====");
		
		//map使用方法引用,输入一个参数,返回一个结果
		Stream<Double> s1 = Stream.of(-1.5, 2.5, -3.5);
		Stream<Double> s2 = s1.map(Math::abs);
		s2.forEach(System.out::println);
		
		System.out.println("======用Lambda表达式对每个元素进行计算=");
		
		//map使用Lambda表达式,输入一个参数,返回一个结果
		Stream<Integer> s3 = Stream.of(1,2,3,4,5);
		Stream<Integer> s4 = s3.map(n->n*n);
		s4.forEach(System.out::println);
		
		System.out.println("======对每个元素进行计算,返回Stream==");
		
		//map使用方法引用,输入一个参数,返回一个Stream
		String[] planets = new String[] { 
				"Mercury", "Venus", "Earth"};
		
		Stream<String> allLetters2 = 
				Stream.of(planets).flatMap(word -> letters(word));
		allLetters2.forEach(System.out::print);
		//flatMap 执行一对多的转换,然后将所有的Map都展开
		//['M','e','r','c','u','r','y',
		// 'V','e','n','u','s',
		// 'E','a','r','t','h']
		
		Stream<Stream<String>> allLetters = 
				Stream.of(planets).map(word -> letters(word));
		allLetters.forEach(System.out::print);
		//[['M','e','r','c','u','r','y'],
		// ['V','e','n','u','s'],
		// ['E','a','r','t','h']]
		
		System.out.println("======对每个元素进行计算,最后综合返回经过合并的Stream==");
		
		
		
	}
	
	public static Stream<String> letters(String word) {
		List<String> result = new ArrayList<>();
		for(int i=0;i<word.length();i++)
		{
			result.add(word.substring(i, i+1));
		}
		return result.stream();
	}
}


6.limit抽取:

		//获取前n个元素
		Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9,10);
		Stream<Integer> s2 = s1.limit(3);
		s2.forEach(System.out::println);

7.skip跳过:

Stream<Integer> s3 = Stream.of(1,2,3,4,5,6,7,8,9,10);
		Stream<Integer> s4 = s3.skip(8);
		s4.forEach(System.out::println);

8.concat连接:

		Stream<String> s5 = Stream.concat(letters("hello"), letters("world"));
		s5.forEach(System.out::println);

9.额外调试peek

import java.util.stream.Stream;

public class StreamOther {

	public static void main(String[] args) {
		Stream<Double> s1 = Stream.iterate(1.0, n -> n*2)
				.peek(n -> System.out.println("number:" + n)).limit(5);
		s1.forEach(System.out::println);

	} 

}
输出:
number:1.0
1.0
number:2.0
2.0
number:4.0
4.0
number:8.0
8.0
number:16.0
16.0

四、Optional类型

Optional<T>介绍

  • 一个包装器对象
  • 要么保证类型T对象,要么没有包装任何对象(还是NULL)
  • 如果T有值,那么直接返回T的对象
  • 如果T是NULL,那么可以返回一个替代物

Optional<T>创建:

  • of方法
  • empty方法
  • ofNullable方法,对于对象可能伟null情况下安全创建

直接用get方法optional为空则会引发NOSuchElementException异常

isPresent判断非常低效一般不用。

五、流的计算结果

流的计算:

  • 简单约简(聚合函数):count/max/min/...
  • 自定义约简:reduce
  • 查看/遍历元素:iterator/forEach
  • 存放到数据结构当中

1.简约约简:

 2.自定义约简:

import java.util.Optional;
import java.util.stream.Stream;

public class Reduce {

	public static void main(String[] args) {
		
		Integer[] a = new Integer[] {2,4,6,8};
		
		Stream<Integer> s1 = Stream.of(a);
		Optional<Integer> sum = s1.reduce(Integer::sum);
		System.out.println(sum.get());
		
		Stream<Integer> s2 = Stream.of(a);
		Optional<Integer> product = s2.reduce((x,y)->x*y);
		System.out.println(product.get());
		
		Stream<Integer> s3 = Stream.of(a);
		Integer product3 = s3.reduce(1,(x,y)->x*y);//给定初始值
		System.out.println(product3);
		
		String[] b = new String[] {"abc","def","ghi"};
		Stream<String> s4 = Stream.of(b);
		String bigStr = s4.reduce("",(x,y)->x+y);
		System.out.println(bigStr);
		

	}

}
输出:
20
384
384
abcdefghi

3.遍历和查看:

import java.util.Iterator;
import java.util.stream.Stream;

public class StreamView {

	public static void main(String[] args) {
		Integer[] a = new Integer[] {2,4,6,8};
		
		Stream<Integer> s1 = Stream.of(a);
		Iterator<Integer> it1 = s1.filter(n->n>2).iterator();
		while(it1.hasNext()) {
			System.out.println(it1.next());
		}
		
		Stream<Integer> s2 = Stream.of(a);
		s2.filter(n->n>2).forEach(System.out::println);
		
		
	}

}

4.存放打数据结构中:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamCollect {

	public static void main(String[] args) {
		Integer[] a = new Integer[] {2,4,6,8};
		
		//将流存储为List
		Stream<Integer> s1 = Stream.of(a);
		List<Integer> list1 = s1.collect(Collectors.toList());
		
		//将流存储为指定的LinkedList
		Stream<Integer> s2 = Stream.of(a);
		List<Integer> list2 = s2.collect(Collectors.toCollection(LinkedList::new));
		
		//将流存储为Set
		Stream<Integer> s3 = Stream.of(a);
		Set<Integer> set1 = s3.collect(Collectors.toSet());
		
		//将流变换为字符流,并连接起来
		Stream<Integer> s4 = Stream.of(a);
		String result = s4.map(String::valueOf).collect(Collectors.joining());
		System.out.println(result); //2468
		
		//将流变换为字符流,并连接起来
		Stream<Integer> s5 = Stream.of(a);
		String result2 = s5.map(String::valueOf).collect(Collectors.joining(","));
		System.out.println(result2); //2,4,6,8
		
		
		
		List<Person> persons = new ArrayList<Person>();
		persons.add(new Person(1, "Jerry"));
		persons.add(new Person(2, "Tom"));
		
		//将流存储为Map
		Stream<Person> s6 = persons.stream();
		Map<Integer, String> map1 = s6.collect(Collectors.toMap(Person::getId, Person::getName));
		for(Integer i:map1.keySet())
		{
			System.out.println("id:" + i + ", name:" + map1.get(i));
		}
		
	}

}

class Person
{
	int id;
	String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Person(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}	
}

六、流的应用

 

 注意事项:

  • 一个流一次只能有一个用途
  • 不要创建无线流
  • 注意流的操作顺序
  • 谨慎使用并行流

并行流的使用前提:

 

 参考中国大学mooc《java核心技术》

发布了55 篇原创文章 · 获赞 17 · 访问量 4987

猜你喜欢

转载自blog.csdn.net/weixin_43698704/article/details/104262899
今日推荐