1. 程式人生 > >Java8 Stream中的常用方法展示

Java8 Stream中的常用方法展示

其實Streanm 和常規的一些操作效能差別並不太大,關鍵在於你如何使用。

  1. 對於簡單操作,比如最簡單的遍歷,Stream序列API效能明顯差於顯示迭代,但並行的Stream API能夠發揮多核特性。
  2. 對於複雜操作,Stream序列API效能可以和手動實現的效果匹敵,在並行執行時Stream API效果遠超手動實現。

所以,如果出於效能考慮,1. 對於簡單操作推薦使用外部迭代手動實現,2. 對於複雜操作,推薦使用Stream API, 3. 在多核情況下,推薦使用並行Stream API來發揮多核優勢,4.單核情況下不建議使用並行Stream API。

如果出於程式碼簡潔性考慮,使用Stream API能夠寫出更短的程式碼。即使是從效能方面說,儘可能的使用Stream API也另外一個優勢,那就是隻要Java Stream類庫做了升級優化,程式碼不用做任何修改就能享受到升級帶來的好處。

package com.whb;

import java.util.Arrays;
import java.util.Comparator;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;

import lombok.Data;

/**
 * @author whb
 * @date 2018年10月29日 下午4:28:07
 * @Description: JDK8的stram方法演示
 * 如果一個方法接受聲明於 java.util.function 包內的介面,例如 Predicate、Function、Consumer 或 Supplier,那麼可以向其傳lambda表示式.
 * lambda表示式內可以使用方法引用,僅當該方法不修改lambda表示式提供的引數。本例中的lambda表示式可以換為方法引用,因為這僅是一個引數相同的簡單方法呼叫。
 */
public class LambdaTest {

	/**
	 * lambda表示式內可以使用方法引用,僅當該方法不修改lambda表示式提供的引數。本例中的lambda表示式可以換為方法引用,因為這僅是一個引數相同的簡單方法呼叫。
	 * list.forEach(n -> System.out.println(n));
	 * list.forEach(System.out::println);  // 使用方法引用
	 * 然而,若對引數有任何修改,則不能使用方法引用,而需鍵入完整地lambda表示式,如下所示:
	 * list.forEach((String s) -> System.out.println("*" + s + "*"));
	 * 事實上,可以省略這裡的lambda引數的型別宣告,編譯器可以從列表的類屬性推測出來。
	 */
	@Test
	public void testForEach() {
		List<Integer> list = Lists.newArrayList();
		list.add(1);
		list.add(2);
		list.add(3);
		// 直接列印
		list.forEach(System.out::println);
		list.forEach(Integer::floatValue);

		// 取值分別操作
		list.stream().forEach(i -> {
			System.out.println(i * 3);
		});

		// 可改變物件,只在本次呼叫中有效, 並不會改變原有的list
		list.stream().map((i) -> i * 3).forEach(System.out::println);

		// 不可改變元有物件
		list.forEach(i -> i = i * 3);
		list.forEach(System.out::println);

		Integer integer = list.stream().map((i) -> i = i * 3).reduce((sum, count) -> sum += count).get();
		System.out.println(integer);
	}
	
	@Test
	public void test2() {
		new Thread(() -> System.out.println("In Java8!")).start();

		System.out.println(Joiner.on("_").join("11", "22", "33"));

		List<String> languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");

		System.out.println("Languages which starts with J :");
		filter(languages, (str) -> ((String) str).startsWith("J"));

		System.out.println("Languages which ends with a ");
		filter(languages, (str) -> ((String) str).endsWith("a"));

		System.out.println("Print all languages :");
		filter(languages, (str) -> true);

		System.out.println("Print no language : ");
		filter(languages, (str) -> false);

		System.out.println("Print language whose length greater than 4:");
		filter(languages, (str) -> ((String) str).length() > 4);

		Predicate<String> startWithJ = (n) -> n.startsWith("J");
		Predicate<String> fourLength = (n) -> n.length() == 4;

		languages.stream().filter(startWithJ.and(fourLength)).forEach(System.out::println);

	}

	/**
	 * redicate介面
	 * @param names
	 * @param condition
	 */
	private void filter(List<String> names, Predicate<String> condition) {
		for (String name : names) {
			if (condition.test(name)) {
				System.out.println(name + " ");
			}
		}
	}
	
	/**
	 * reduce, 用來將值進行合併, 又稱摺疊操作, Map和Reduce操作是函數語言程式設計的核心操作
	 * SQL中類似 sum()、avg() 或者 count() 的聚集函式,實際上就是 reduce 操作,因為它們接收多個值並返回一個值。
	 * 流API定義的 reduceh() 函式可以接受lambda表示式,並對所有值進行合併。
	 * IntStream這樣的類有類似 average()、count()、sum() 的內建方法來做 reduce 操作,
	 * 也有mapToLong()、mapToDouble() 方法來做轉換
	 */
	@Test
	public void test3() {
		// 字串連線,concat = "ABCD"
		String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
		System.out.println(concat);
		// 求最小值,minValue = -3.0
		double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
		System.out.println(minValue);
		// 求和,sumValue = 10, 有起始值
		int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
		System.out.println(sumValue);
		// 求和,sumValue = 10, 無起始值
		sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
		// 過濾,字串連線,concat = "ace"
		concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
		System.out.println("a".concat("b"));
	}

	
	

	/**
	 * 過濾是Java開發者在大規模集合上的一個常用操作,而現在使用lambda表示式和流API過濾大規模資料集合是驚人的簡單。
	 * 流提供了一個 filter() 方法,接受一個 Predicate 物件,即可以傳入一個lambda表示式作為過濾邏輯。
	 * 下面的例子是用lambda表示式過濾Java集合,將幫助理解。 
	 */
	@Test
	public void test4() {
		List<String> strList = Arrays.asList("abc", "eqwr", "bcd", "qb", "ehdc", "jk");
		List<String> filtered = strList.stream().filter(x -> x.length() > 2).collect(Collectors.toList());
		System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);

		// 對列表的每個元素使用 函式
		strList = Lists.newArrayList("abc", "eqwr", "bcd", "qb", "ehdc", "jk");
		String collect = strList.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
		System.out.printf("filtered list : %s %n", collect);

		// 使用distinct進行去重
		List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
		List<Integer> distinct = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList());
		System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);
	}
	
	/**
	 * 計算最值和平均值
	 * IntStream、LongStream 和 DoubleStream 等流的類中,有個非常有用的方法叫做 summaryStatistics() 。
	 * 可以返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各種摘要資料。
	 * 在本例中,我們用這個方法來計算列表的最大值和最小值。它也有 getSum() 和 getAverage() 方法來獲得列表的所有元素的總和及平均值
	 */
	@Test
	public void test5() {
		// 獲取數字的個數、最小值、最大值、總和以及平均值
		List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
		IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
		System.out.println("Highest prime number in List : " + stats.getMax());
		System.out.println("Lowest prime number in List : " + stats.getMin());
		System.out.println("Sum of all prime numbers : " + stats.getSum());
		System.out.println("Average of all prime numbers : " + stats.getAverage());
	}
	
	
	/**
	 * 使用方法引用, 不對引數做任何修改方法引用有3種形式
     * 把lambda表示式的引數直接當成instanceMethod|staticMethod的引數來呼叫。
     * 比如System.out::println等同於x->System.out.println(x);Math::max等同於(x, y)->Math.max(x,y)。
	 * objectName::instanceMethod
	 * ClassName::staticMethod
	 * 把lambda表示式的第一個引數當成instanceMethod的目標物件,其他剩餘引數當成該方法的引數。
	 * 比如String::toLowerCase等同於x->x.toLowerCase()。
	 * ClassName::instanceMethod
	 */
	@Test
	public void test6() {

		// 使用String預設的排序規則,比較的是Person的name欄位
		Comparator<Student> byName = Comparator.comparing(p -> p.getName());
		// 不用寫傳入引數,傳入的用Person來宣告
		Comparator<Student> byName2 = Comparator.comparing(Student::getName);

		List<Student> studentList = Arrays.asList(new Student("網三", 11), new Student("彰武", 22), new Student("趙四", 33));

		// 獲取執行緒安全的Map
		ConcurrentHashMap<String, Student> currrentMap = (ConcurrentHashMap<String, Student>) studentList.stream()
				.collect(Collectors.toConcurrentMap(Student::getName, Student -> Student));

		// 老的方式
		Map<String, Integer> map = studentList.stream().collect(Collectors.toMap(new Function<Student, String>() {
			@Override
			public String apply(Student t) {
				return t.getName();
			}
		}, Student::getAge));

	}
	
	/**
	 * 實體類
	 */
	@Data
	class Student {
		private String name;
		private Integer age;

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