Java基礎——函數式編程

函數式編程

  • 將函數作爲基本運算單元
  • 函數可以作爲變量
  • 函數可以接收函數,可以返回函數
  • 研究函數式編程的理論是λ演算,所以將函數式編程的風格稱爲Lambda表達式
Arrays.sort(array, (s1,s2)->{	// 參數
	return s1.compareTo(s2);	// 函數體
})
  • 如上所示,可以簡化語法(簡化單抽象方法實現),類型自動推斷
  • 只定義了單個抽象方法的接口可以被標註爲@FunctionalInterface,單抽象方法接口也被稱爲函數式接口
  • 即函數式編程涉及到的接口均是這類函數式接口,例如interface Comparator<>

方法引用

  • 如果某個方法簽名(參數類型和返回值)和接口恰好一致,就可以直接傳入方法引用
  • 引用格式:類名::方法名
  • 靜態方法引用:
    在這裏插入圖片描述

實現Comparator接口的類的對象的列表或數組可以用Array.sort()方法排序,這裏的排序規則引用了自定義的靜態方法

  • 實例方法引用:
    在這裏插入圖片描述

實例方法的調用必須傳入一個隱藏的實例變量,例如這裏的instance.compareToIgnoreCase(s)
就是說我們實例化的方法都存在一個隱藏的this變量,也是經常使用的

  • 構造方法引用
    在這裏插入圖片描述

.map()需要傳入一個返回值爲Person的方法,Person的構造方法返回this,正好符合

  • 可以認爲這裏所說的接口就是被操作對象(數據)所使用的接口或者類
import java.util.Arrays;

class SortedBy {
	static int name(String s1, String s2) {
		return s1.compareTo(s2);
	}
	static int nameIgnoreCase(String s1, String s2) {
		return s1.toLowerCase().compareTo(s2.toLowerCase());
	}
	static int length(String s1, String s2) {
		int n1 = s1.length();
		int n2 = s2.length();
		if (n1 == n2) {
			return s1.compareTo(s2);
		}
		return n1 < n2 ? -1 : 1;
	}
}

public class LambdaSort {
	public static void main(String[] args) throws Exception {
		String[] array = "Java Apple lambda functional OOP".split(" ");
		// 靜態方法引用,方法簽名正好符合Comparator接口
		Arrays.sort(array, SortedBy::name);		// SortedBy::nameIgnoreCase
		System.out.println(Arrays.toString(array));
	}
}

Stream

  • Java8引入的API,定義在java.util.stream
  • 對比:
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 特點
  1. 可以存儲有限個或無限個元素,這裏的存儲可以存在內存中或者實時計算產生
  2. 可以轉換爲另一個stream
  3. 惰性計算(通常發生在最後結果的獲取)
    在這裏插入圖片描述
  • 創建stream
  1. 通過指定元素/現有數組/現有Collection創建
// 指定元素:Stream.of(...)
// 現有數組:
import java.util.Arrays;

public class StreamBasic {
	public static void main(String[] args) throws Exception {
		String[] array = "JDK Stream API supports functional-style operations".split(" ");
		// array -> stream:
		long n = Arrays.stream(array)	// 創建固定大小的stream
				// .filter((s) -> s.equals(s.toUpperCase()))	// 純大寫字符串
				.count();
		System.out.println("How many words? " + n);
	}
}
// 現有Collection:collection.stream()
  1. 通過傳入Supplier創建無限序列
Stream<T> s = Stream.generate(Supplier<T> s);	// 通過傳入supplier對象不斷產生內容
// 這種對象保存的是算法
  1. 通過其他類的相關方法創建(Stream API)
    在這裏插入圖片描述

由於不能使用Stream<int> s這樣的方式創建基本類型的stream,java提供了以下類型:
在這裏插入圖片描述

Stream.map

  • 將一個stream轉換爲另一個,兩個是stream按照映射函數一一對應
import java.util.Arrays;
import java.util.stream.Stream;

public class StreamMapSample {
	public static void main(String[] args) throws Exception {
		String[] array = "Stream API supports functional-style operations".split(" ");
		Stream<String> stream = Arrays.stream(array);
		stream.map(String::toUpperCase).forEach(System.out::println);// 方法引用作爲映射函數
	}
}
import java.util.Arrays;
import java.util.stream.Stream;

class Person {
	String name;
	char gender;
	public Person(String name, char gender) {
		this.name = name;
		this.gender = gender;
	}
	public String toString() {
		return "Person(" + name + ", " + gender + ")";
	}
}

public class StreamMapSample2 {
	public static void main(String[] args) throws Exception {
		String[] inputs = { "Bob,M", "Alice,F", "Time,M", "Lily,F" };
		Stream<String> names = Arrays.stream(inputs);
		Stream<Person> persons = names.map((s) -> {// 可以將一種元素類型轉化爲另一種元素類型
			int n = s.indexOf(',');
			String name = s.substring(0, n);
			char gender = s.charAt(n + 1);
			return new Person(name, gender);
		});
		persons.forEach(System.out::println);
	}
}

Stream.fliter

  • 排除不滿足條件的元素
import java.util.function.Supplier;
import java.util.stream.Stream;

class NaturalSupplier implements Supplier<Long> {
	long x = 0;

	public Long get() {	// 默認調用get方法
		x++;
		return x;
	}

}

public class StreamFilterSample {
	public static void main(String[] args) throws Exception {
		Stream<Long> natural = Stream.generate(new NaturalSupplier());// 傳入算法,得到無限序列
		Stream<Long> odd = natural.filter((n) -> n % 2 == 1);
		odd.limit(20).forEach(System.out::println);	// 限制只產生20個
	}
}

Stream.reduce

  • stream的聚合方法,把一個stream的所有元素聚合成一個結果
    在這裏插入圖片描述
  • acc和n分別被前兩個元素初始化
  • acc是上一次計算的結果,n是stream中下一個值
    在這裏插入圖片描述
import java.util.stream.Stream;

public class StreamReduceSample {
	public static void main(String[] args) throws Exception {
		int r = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce((acc, x) -> acc * x).get();
		System.out.println(r);
	}
}
import java.util.Arrays;

public class StreamReduceSample2 {
	public static void main(String[] args) throws Exception {
		String[] array = "Stream API supports functional-style operations".split(" ");
		String result = Arrays.stream(array).map(String::toLowerCase).reduce((acc, s) -> acc + " ~ " + s).get();
		System.out.println(result);
	}
}
  • reduce方法會立刻對stream進行計算

其他常用操作

在這裏插入圖片描述

結語

對java基礎的學習到這裏告一段落,可以體會到Java生態的龐大,我們可以藉助它輕鬆地完成很多工作,Java解決問題的思路有很多值得學習的地方,需要在實踐中多加積累!
後面會學習記錄一些Java較深層次的原理,路漫漫其修遠兮…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章