1. 程式人生 > >JAVA8新特性-Stream API,函數語言程式設計

JAVA8新特性-Stream API,函數語言程式設計

首先先拿github上的兩篇文章作為關於Stream API的專業性技術指導

http://www.cnblogs.com/CarpenterLee/p/6545321.html   Java Stream API入門篇

http://www.cnblogs.com/CarpenterLee/p/6637118.html   Java Stream API進階篇

——————————————————————————————————————

why?

Java8中有兩大最為重要的改變。第一個是 Lambda 表示式;另外一 個則是 Stream API(java.util.stream.*)。 Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對 集合進行的操作,可以執行非常複雜的查詢、過濾和對映資料等操作。 使用Stream API 對集合資料進行操作,就類似於使用 SQL 執行的數 據庫查詢。也可以使用 Stream API 來並行執行操作。簡而言之, Stream API 提供了一種高效且易於使用的處理資料的方式。

what?

流(Stream) 到底是什麼呢? 是資料渠道,用於操作資料來源(集合、陣列等)所生成的元素序列。 “集合講的是資料,流講的是計算!”

注意: ①Stream 自己不會儲存元素。

②Stream 不會改變源物件。相反,他們會返回一個持有結果的新Stream。

③Stream 操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。

how?

Stream 的操作三個步驟

 建立 Stream 一個數據源(如:集合、陣列),獲取一個流

 中間操作 一箇中間操作鏈,對資料來源的資料進行處理

 終止操作(終端操作) 一個終止操作,執行中間操作鏈,併產生結果

1)建立Stream的流的四種方式

①Java8 中的 Collection 介面被擴充套件,提供了 兩個獲取流的方法:

 default Stream stream() : 返回一個順序流

 default Stream parallelStream() : 返回一個並行流

所以Collection介面下的所有實現類或者子介面都可以通過 物件.stream();的方法返回一個流給Stream物件。

②由陣列建立流

Java8 中的 Arrays 的靜態方法 stream() 可 以獲取陣列流:

static Stream stream(T[] array): 返回一個流 過載形式,能夠處理對應基本型別的陣列:

public static IntStream stream(int[] array)

 public static LongStream stream(long[] array)

 public static DoubleStream stream(double[] array)

所以從陣列中獲取流可以使用 Arrays.stream(陣列名);來進行獲取

③由值建立流

可以使用Stream類的靜態方法 Stream.of(),通過顯示值 建立一個流。它可以接收任意數量的引數。

public static Stream of(T... values) : 返回一個流

可以直接進行Stream stream = Stream.of("a","b","c");獲取

④由函式建立流:建立無限流

可以使用靜態方法 Stream.iterate() 和 Stream.generate(), 建立無限流。

 迭代 public static Stream iterate(final T seed, final UnaryOperator f)

 生成 public static Stream generate(Supplier s) :

/*
 * 一、Stream API 的操作步驟:
 * 
 * 1. 建立 Stream
 * 
 * 2. 中間操作
 * 
 * 3. 終止操作(終端操作)
 */
public class TestStreamaAPI {
	
	//1. 建立 Stream
	@Test
	public void test1(){
		//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的中間操作

多箇中間操作可以連線起來形成一個流水線,除非流水 線上觸發終止操作,否則中間操作不會執行任何的處理! 而在終止操作時一次性全部處理,稱為“惰性求值”。

①篩選與切片

public class Test{
//2. 中間操作
	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)
	);
	
	/*
	  篩選與切片
		filter——接收 Lambda , 從流中排除某些元素。
		limit——截斷流,使其元素不超過給定數量。
		skip(n) —— 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補
		distinct——篩選,通過流所生成元素的 hashCode() 和 equals() 去除重複元素
	 */
	
	//內部迭代:迭代操作 Stream API 內部完成
	@Test
	public void test2(){
		//所有的中間操作不會做任何的處理
		Stream<Employee> stream = emps.stream()
			.filter((e) -> {
				System.out.println("測試中間操作");
				return e.getAge() <= 35;
			});
		
		//只有當做終止操作時,所有的中間操作會一次性的全部執行,稱為“惰性求值”
		stream.forEach(System.out::println);
	}
	
	//外部迭代
	@Test
	public void test3(){
		Iterator<Employee> it = emps.iterator();
		
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
	
	@Test
	public void test4(){
		emps.stream()
			.filter((e) -> {
				System.out.println("短路!"); // &&  ||
				return e.getSalary() >= 5000;
			}).limit(3)
			.forEach(System.out::println);
	}
	
	@Test
	public void test5(){
		emps.parallelStream()
			.filter((e) -> e.getSalary() >= 5000)
			.skip(2)
			.forEach(System.out::println);
	}
	
	@Test
	public void test6(){
		emps.stream()
			.distinct()
			.forEach(System.out::println);
	}
}

②對映

//2. 中間操作
	/*
		對映
		map——接收 Lambda , 將元素轉換成其他形式或提取資訊。接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素。
		flatMap——接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流
	 */
	@Test
	public void test1(){
		Stream<String> str = emps.stream()
			.map((e) -> e.getName());
		
		System.out.println("-------------------------------------------");
		
		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
		
		Stream<String> stream = strList.stream()
			   .map(String::toUpperCase);
		
		stream.forEach(System.out::println);
		
		Stream<Stream<Character>> stream2 = strList.stream()
			   .map(TestStreamAPI1::filterCharacter);
		
		stream2.forEach((sm) -> {
			sm.forEach(System.out::println);
		});
		
		System.out.println("---------------------------------------------");
		
		Stream<Character> stream3 = strList.stream()
			   .flatMap(TestStreamAPI1::filterCharacter);
		
		stream3.forEach(System.out::println);
	}

	public static Stream<Character> filterCharacter(String str){
		List<Character> list = new ArrayList<>();
		
		for (Character ch : str.toCharArray()) {
			list.add(ch);
		}
		
		return list.stream();
	}

③排序

/*
		sorted()——自然排序
		sorted(Comparator com)——定製排序
	 */
	@Test
	public void test2(){
		emps.stream()
			.map(Employee::getName)
			.sorted()
			.forEach(System.out::println);
		
		System.out.println("------------------------------------");
		//定製排序
		emps.stream()
			.sorted((x, y) -> {
				if(x.getAge() == y.getAge()){
					return x.getName().compareTo(y.getName());
				}else{
					return Integer.compare(x.getAge(), y.getAge());
				}
			}).forEach(System.out::println);
	}

3)Stream的終止操作

終端操作會從流的流水線生成結果。其結果可以是任何不是流的 值,例如:List、Integer,甚至是 void 。

①查詢與匹配

public class TestStreamAPI2 {
	
	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)
	);
	
	//3. 終止操作
	/*
		allMatch——檢查是否匹配所有元素
		anyMatch——檢查是否至少匹配一個元素
		noneMatch——檢查是否沒有匹配的元素
		findFirst——返回第一個元素
		findAny——返回當前流中的任意元素
		count——返回流中元素的總個數
		max——返回流中最大值
		min——返回流中最小值
	 */
	@Test
	public void test1(){
			boolean bl = emps.stream()
				.allMatch((e) -> e.getStatus().equals(Status.BUSY));
			
			System.out.println(bl);
			
			boolean bl1 = emps.stream()
				.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
			
			System.out.println(bl1);
			
			boolean bl2 = emps.stream()
				.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
			
			System.out.println(bl2);
	}
	
	@Test
	public void test2(){
		Optional<Employee> op = emps.stream()
			.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
			.findFirst();
		
		System.out.println(op.get());
		
		System.out.println("--------------------------------");
		
		Optional<Employee> op2 = emps.parallelStream()
			.filter((e) -> e.getStatus().equals(Status.FREE))
			.findAny();
		
		System.out.println(op2.get());
	}
	
	@Test
	public void test3(){
		long count = emps.stream()
						 .filter((e) -> e.getStatus().equals(Status.FREE))
						 .count();
		
		System.out.println(count);
		
		Optional<Double> op = emps.stream()
			.map(Employee::getSalary)
			.max(Double::compare);
		
		System.out.println(op.get());
		
		Optional<Employee> op2 = emps.stream()
			.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
		
		System.out.println(op2.get());
	}
	
	//注意:流進行了終止操作後,不能再次使用
	@Test
	public void test4(){
		Stream<Employee> stream = emps.stream()
		 .filter((e) -> e.getStatus().equals(Status.FREE));
		
		long count = stream.count();
		
		stream.map(Employee::getSalary)
			.max(Double::compare);
	}
}

②歸約

List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 79, 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)
	);
	
	//3. 終止操作
	/*
		歸約
		reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以將流中元素反覆結合起來,得到一個值。
	 */
	@Test
	public void test1(){
		List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
		
		Integer sum = list.stream()
			.reduce(0, (x, y) -> x + y);
		//累加得到和
		System.out.println(sum);
		
		System.out.println("----------------------------------------");
		
		Optional<Double> op = emps.stream()
			.map(Employee::getSalary)
			.reduce(Double::sum);
		
		System.out.println(op.get());
	}
	
	//需求:搜尋名字中 “六” 出現的次數
	@Test
	public void test2(){
		Optional<Integer> sum = emps.stream()
			.map(Employee::getName)
			.flatMap(TestStreamAPI1::filterCharacter)
			.map((ch) -> {
				if(ch.equals('六'))
					return 1;
				else 
					return 0;
			}).reduce(Integer::sum);
		
		System.out.println(sum.get());
	}

③收集

下列是Collector介面中的方法

//collect——將流轉換為其他形式。接收一個 Collector介面的實現,用於給Stream中元素做彙總的方法
	@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);
	}
	
	@Test
	public void test4(){
		Optional<Double> max = emps.stream()
			.map(Employee::getSalary)
			.collect(Collectors.maxBy(Double::compare));
		
		System.out.println(max.get());
		
		Optional<Employee> op = emps.stream()
			.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
		
		System.out.println(op.get());
		
		Double sum = emps.stream()
			.collect(Collectors.summingDouble(Employee::getSalary));
		
		System.out.println(sum);
		
		Double avg = emps.stream()
			.collect(Collectors.averagingDouble(Employee::getSalary));
		
		System.out.println(avg);
		
		Long count = emps.stream()
			.collect(Collectors.counting());
		
		System.out.println(count);
		
		System.out.println("--------------------------------------------");
		
		DoubleSummaryStatistics dss = emps.stream()
			.collect(Collectors.summarizingDouble(Employee::getSalary));
		
		System.out.println(dss.getMax());
	}
	
	//分組
	@Test
	public void test5(){
		Map<Status, List<Employee>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus));
		
		System.out.println(map);
	}
	
	//多級分組
	@Test
	public void test6(){
		Map<Status, Map<String, List<Employee>>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
				if(e.getAge() >= 60)
					return "老年";
				else if(e.getAge() >= 35)
					return "中年";
				else
					return "成年";
			})));
		
		System.out.println(map);
	}
	
	//分割槽
	@Test
	public void test7(){
		Map<Boolean, List<Employee>> map = emps.stream()
			.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
		
		System.out.println(map);
	}
	
	//
	@Test
	public void test8(){
		String str = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.joining("," , "----", "----"));
		
		System.out.println(str);
	}
	
	@Test
	public void test9(){
		Optional<Double> sum = emps.stream()
			.map(Employee::getSalary)
			.collect(Collectors.reducing(Double::sum));
		
		System.out.println(sum.get());
	}