Java8的lambda表達式和Stream API

一直在用JDK8 ,卻從未用過Stream,爲了對數組或集合進行一些排序、過濾或數據處理,只會寫for循環或者foreach,這就是我曾經的一個寫照。java

 

剛開始寫寫是打基礎,但寫的多了,各類乏味,非過來人不能感同身受。今天,我就要分享一篇如何解決上述問題的新方法 - Stream API。但學習Stream以前卻不得不學一下Lambda表達式。說實話,網上介紹Lambda表達式的文章不少,大多晦澀難懂,今天我就想用本身的理解去說一下Lambda表達式是如何讓咱們的代碼寫的更少!算法

 

02來自IDEA的提示

在IDE中,你是否遇到在寫如下列代碼時,被友情提示的狀況:數組

new Thread(new Runnable() {
    @Override
    public void run() {
          System.out.println("thread");
    }
});

  

這時候,咱們按一下快捷鍵,IDE自動幫咱們把代碼優化爲醬個樣子:數據結構

new Thread(() -> System.out.println("thread"));

  

這就是Java8的新特性:lambda表達式。app

 

03Lambda入門

 

借用上面的示例,在調用new Thread的含參構造方法時,咱們經過匿名內部類的方式實現了Runnable對象,但其實有用的代碼只有System.out.println("thread")這一句,而咱們卻要爲了這一句去寫這麼多行代碼。正是這個問題,纔有了Java8中的lambda表達式。那lambd表達式到底是如何簡化代碼的呢?dom

 

先來看lambda表達式的語法:ide

() -> {}

(): 括號就是接口方法的括號,接口方法若是有參數,也須要寫參數。只有一個參數時,括號能夠省略。函數

->: 分割左右部分的,沒啥好說的。學習

{} : 要實現的方法體。只有一行代碼時,能夠不加括號,能夠不寫return。優化

 

看了上面的解釋,也就不難理解IDE優化後的代碼了。不過看到這裏你也許意識到,若是接口中有多個方法時,按照上面的邏輯lambda表達式恐怕不行了。沒錯,lambda表達式只適用於函數型接口。說白了,函數型接口就是隻有一個抽象方法的接口。這種類型的接口還有一個對應的註解:@FunctionalInterface。爲了讓咱們在須要這種接口時再也不本身去建立,Java8中內置了四大核心函數型接口。

 

消費型接口(有參無返回值)

Consumer<T>

void accept(T t);
供給型接口(無參有返回值)

Supplier<T>

T get();
函數型接口(有參有返回值)

Function<T, R>

R apply(T t);
斷言型接口(有參有布爾返回值)

Predicate<T>

boolean test(T t);

  

看到這裏若是遇到通常的lambda表達式,你應該能夠從容面對了,但高級點的恐怕看到仍是懵,不要急,其實也不難。Lambda表達式還有兩種簡化代碼的手段,分別是方法引用構造引用

 

04方法引用

 

方法引用是什麼呢?若是咱們要實現接口的方法與另外一個方法A相似,(這裏的相似是指參數類型與返回值部分相同),咱們直接聲明A方法便可。也就是,再也不使用lambda表達式的標準形式,改用高級形式。不管是標準形式仍是高級形式,都是lambda表達式的一種表現形式。

舉例:

Function function1 = (x) -> x;
Function function2 = String::valueOf;

  

對比Function接口的抽象方法與String的value方法,能夠看到它們是相似的。

R apply(T t);
    
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

  

方法引用的語法:

對象::實例方法
類::靜態方法
類::實例方法

前兩個很容易理解,至關於對象調用實例方法,類調用靜態方法同樣。只是第三個須要特殊說明。

當出現以下這種狀況時:

Compare<Boolean> c = (a, b) -> a.equals(b);

用lambda表達式實現Compare接口的抽象方法,而且方法體只有一行,且該行代碼爲參數1調用方法傳入參數2。此時,就能夠簡化爲下面這種形式:

Compare<Boolean> c = String::equals;

也就是「類::實例方法」的形式。

值得一提的是,當參數b不存在時,該方式依舊適用。例如:

Function function1 = (x) -> x.toString();
Function function1 = Object::toString;

05構造引用

 

先來建立一個供給型接口對象:

Supplier<String> supplier = () -> new String();

  

在這個lammbda表達式中只作了一件事,就是返回一個新的String對象,而這種形式能夠更簡化:

Supplier<String> supplier = String::new;

  

提煉一下構造引用的語法:

類名::new
當經過含參構造方法建立對象,而且參數列表與抽象方法的參數列表一致,也就是下面的這種形式:

Function1 function = (x) -> new String(x);
也能夠簡化爲:

Function1 function = String::new;
特殊點的數組類型:

Function<Integer,String[]> function = (x) -> new String[x];

  

能夠簡化爲:

Function<Integer,String[]> function = String[]::new;

 

06Lambda總結

 

上面並無給出太多的lambda實例,只是側重講了如何去理解lambda表達式。到這裏,不要懵。要記住lambda的本質:爲函數型接口的匿名實現進行簡化與更簡化。

所謂的簡化就是lambda的標準形式,所謂的更簡化是在標準形式的基礎上進行方法引用和構造引用。

方法引用是拿已有的方法去實現此刻的接口。

構造引用是對方法體只有一句new Object()的進一步簡化。

 

 

07Stream理解

 

 

如何理解Stream?在我看來,Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。簡單來講,它的做用就是經過一系列操做將數據源(集合、數組)轉化爲想要的結果。

 

Stream有三點很是重要的特性:

  1. Stream 是不會存儲元素的。

  2. Stream 不會改變原對象,相反,他們會返回一個持有結果的新Stream。

  3. Stream 操做是延遲執行的。意味着它們會等到須要結果的時候才執行。

 

08Stream生成

 

 

Collection系的 stream() 和 parallelStream()

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Stream<String> stringStream = list.parallelStream();

  

經過Arrays.stram()

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

  

經過Stream.of()

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

  

經過Stream.iterate()生成無限流

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

  

經過Stream.generate()

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

 

09Stream中間操做

 

多箇中間操做鏈接而成爲流水線,流水線不遇到終止操做是不觸發任何處理的,所爲又稱爲「惰性求值」。

list.stream()
            .map(s -> s + 1)  //映射
            .flatMap(s -> Stream.of(s)) //和map差很少,但返回類型爲Stream,相似list.add()和list.addAll()的區別
            .filter(s -> s < 1000)    //過濾
            .limit(5)   //限制
            .skip(1)    //跳過
            .distinct() //去重
            .sorted()   //天然排序
            .sorted(Integer::compareTo) //自定義排序

  

關於map方法,參數爲一個Function函數型接口的對象,也就是傳入一個參數返回一個對象。這個參數就是集合中的每一項。相似Iterator遍歷。其它的幾個操做思想都差很少。

執行上面的方法沒什麼用,由於缺乏終止操做。

 

10Stream的終止操做

 

經常使用的終止API以下:

list.stream().allMatch((x) -> x == 555); // 檢查是否匹配全部元素
list.stream().anyMatch(((x) -> x>600)); // 檢查是否至少匹配一個元素
list.stream().noneMatch((x) -> x>500); //檢查是否沒有匹配全部元素
list.stream().findFirst(); // 返回第一個元素
list.stream().findAny(); // 返回當前流中的任意一個元素
list.stream().count(); // 返回流中元素的總個數
list.stream().forEach(System.out::println); //內部迭代
list.stream().max(Integer::compareTo); // 返回流中最大值
Optional<Integer> min = list.stream().min(Integer::compareTo);//返回流中最小值
System.out.println("min "+min.get());

  

reduce (歸約):將流中元素反覆結合起來獲得一個值

Integer reduce = list.stream()
        .map(s -> (s + 1))
        .reduce(0, (x, y) -> x + y);    //歸約:0爲第一個參數x的默認值,x是計算後的返回值,y爲每一項的值。
System.out.println(reduce);

Optional<Integer> reduce1 = list.stream()
        .map(s -> (s + 1))
        .reduce((x, y) -> x + y);  // x是計算後的返回值,默認爲第一項的值,y爲其後每一項的值。
System.out.println(reduce);

  

collect(收集):將流轉換爲其餘形式。須要Collectors類的一些方法。

//轉集合
Set<Integer> collect = list.stream()
    .collect(Collectors.toSet());

List<Integer> collect2 = list.stream()
    .collect(Collectors.toList());

HashSet<Integer> collect1 = list.stream()
    .collect(Collectors.toCollection(HashSet::new));

//分組 {group=[444, 555, 666, 777, 555]}
Map<String, List<Integer>> collect3 = list.stream()
    .collect(Collectors.groupingBy((x) -> "group"));//將返回值相同的進行分組
System.out.println(collect3);

//多級分組 {group={777=[777], 666=[666], 555=[555, 555], 444=[444]}}
Map<String, Map<Integer, List<Integer>>> collect4 = list.stream()
    .collect(Collectors.groupingBy((x) -> "group", Collectors.groupingBy((x) -> x)));
System.out.println(collect4);

//分區 {false=[444], true=[555, 666, 777, 555]}
Map<Boolean, List<Integer>> collect5 = list.stream()
    .collect(Collectors.partitioningBy((x) -> x > 500));
System.out.println(collect5);

//彙總
DoubleSummaryStatistics collect6 = list.stream()
    .collect(Collectors.summarizingDouble((x) -> x));
System.out.println(collect6.getMax());
System.out.println(collect6.getCount());

//拼接 444,555,666,777,555
String collect7 = list.stream()
    .map(s -> s.toString())
    .collect(Collectors.joining(","));
System.out.println(collect7);

 

java8 新特性 Stream流 分組 排序 過濾 多條件去重


public class Streams {
	
	private static List<User> list = new ArrayList<User>();
	
	public static void main(String[] args) {
		list =  Arrays.asList(
				new User(1, "a", 10),
				new User(4, "d", 19),
				new User(5, "e", 13),
				new User(2, "b", 14),
				new User(3, "a", 10),
				new User(6, "f", 16)
				);
		
		long start = System.currentTimeMillis();
		
		order();
		
		println(String.format("耗時[%s]毫秒", (System.currentTimeMillis() - start)));
		
	}
	
	/**
	 * 多條件去重
	 * @param list
	 */
	public static void order() {
		list.stream().collect(Collectors.collectingAndThen(
				Collectors.toCollection(() -> new TreeSet<>(
						Comparator.comparing(o -> o.getAge() + ";" + o.getName()))), ArrayList::new)).forEach(u -> println(u));
	}
	
	public static void group() {
		Map<Integer, List<User>> collect = list.stream().collect(Collectors.groupingBy(User::getAge));
		System.out.println(collect);
	}
	
	/**
	 * filter過濾
	 * @param list
	 */
	public static void filterAge() {
		list.stream().filter(u -> u.getAge() == 10).forEach(u -> println(u));
	}
	
	/**
	 * sorted排序
	 */
	public static void stord() {
		list.stream().sorted(Comparator.comparing(u-> u.getAge())).forEach(u -> println(u));
		
	}
	/**
	 * limit方法限制最多返回多少元素
	 */
	public static void limit() {
		list.stream().limit(2).forEach(u -> println(u));
	}
	/**
	 * 不要前多n個元素,n大於知足條件的元素個數就返回空的流
	 */
	public static void skip() {
		list.stream().skip(2).forEach(u -> println(u));
	}
	// 最大值 最小值
	public static void statistics() {
		Optional<User> min = list.stream().min(Comparator.comparing(User::getUserId));
		println(min);
		Optional<User> max = list.stream().max(Comparator.comparing(User::getUserId));
		println(max);
	}
	
	// 統計
	public static void summarizingInt(){
		IntSummaryStatistics statistics = list.stream().collect(Collectors.summarizingInt(User::getAge));
		double average = statistics.getAverage();
		long count = statistics.getCount();
		int max = statistics.getMax();
		int min = statistics.getMin();
		long sum = statistics.getSum();
		println(average);
		println(count);
		println(min);
		println(sum);
		println(max);
		
	}
	/**
	 * 轉set
	 */
	public static void toSet() {
		Set<User> collect = list.stream().collect(Collectors.toSet());
		Iterator<User> iterator = collect.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next().getUserId());
		}
	}

	/**
	 * 轉map
	 */
	public static void toMap() {
		Map<Integer, User> collect = list.stream().collect(Collectors.toMap(User::getUserId, u -> u));
		for (Integer in : collect.keySet()) {
		  User u = collect.get(in);//獲得每一個key多對用value的值
		  println(u);
		}
	}
	/**
	 *map
	 */
	public static void map() {
		list.stream().map(User::getUserId).forEach(userId -> println(userId));
		list.stream().mapToInt(User::getAge).forEach(userId -> println(userId));
		list.stream().mapToDouble(User::getUserId).forEach(userId -> println(userId));
		list.stream().mapToLong(User::getUserId).forEach(userId -> println(userId));
	}
	
	/**
	 * 查找與匹配
	 * allMatch方法與anyMatch差很少,表示全部的元素都知足才返回true。noneMatch方法表示沒有元素知足
	 */
	public static void anyMatch() {
		boolean anyMatch = list.stream().anyMatch(u -> u.getAge() == 100);
		boolean allMatch = list.stream().allMatch(u -> u.getUserId() == 10);
		boolean noneMatch = list.stream().noneMatch(u -> u.getUserId() == 10);
		println(anyMatch);
		println(allMatch);
		println(noneMatch);
	}
	
	/**
	 * reduce操做
	 */
	public static void reduce() {
		Optional<Integer> sum = list.stream().map(User::getAge).reduce(Integer::sum);
		Optional<Integer> max = list.stream().map(User::getAge).reduce(Integer::max);
		Optional<Integer> min = list.stream().map(User::getAge).reduce(Integer::min);
		println(sum);
		println(max);
		println(min);
	}
	/**
	 * 公共輸出
	 * @param c
	 */
	public static void println(Object c) {
		System.out.println(c.toString());
	}
	

	static class User {
		private Integer userId;
		
		private String name;
		
		private Integer age;
		
		User(Integer userId, String name, Integer age) {
			super();
			this.userId = userId;
			this.name = name;
			this.age = age;
		}
		
		public Integer getUserId() {
			return userId;
		}

		public void setUserId(Integer userId) {
			this.userId = userId;
		}

		public String getName() {
			return this.name;
		}

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

		public Integer getAge() {
			return age;
		}

		public void setAge(Integer age) {
			this.age = age;
		}

		@Override
		public String toString() {
			return "[userId=" + userId + ", name=" + name + ", age=" + age + "]";
		}
		
	}
相關文章
相關標籤/搜索