Java 8 的新特性,有哪些?
- 為了更易于創(chuàng)建函數(shù)對(duì)象文留,加入了 functional interfaces, lambdas 和 method references
- 為了處理序列數(shù)據(jù)元素泞边,加入了 Streams API
解釋一下 function types
- 只含有一個(gè)抽象方法的接口或抽象類
解釋一下 function objects
- function types的實(shí)例,相當(dāng)于 functions 或者 actions
解釋一下 functional interfaces
- 只含有一個(gè)抽象方法的接口
在創(chuàng)建函數(shù)對(duì)象時(shí)裙犹,為什么優(yōu)先使用lambdas,而不使用匿名類
- 使用匿名類的實(shí)例作為函數(shù)對(duì)象衔憨,已經(jīng)過時(shí)
- 匿名類冗長叶圃,Lambdas 支持類型推斷,更簡潔
舉幾個(gè)例子践图,說明Lambdas比匿名類好用
Collections.sort(words, new Comparator<String>() {
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});
Collections.sort(words,
(s1, s2) -> Integer.compare(s1.length(), s2.length()));
Collections.sort(words, comparingInt(String::length));
words.sort(comparingInt(String::length));
使用Lambdas的注意事項(xiàng)
- 由于Lambdas 缺少 names 和 documentation掺冠,如果一次計(jì)算不是自解釋性的夹供,或者超過3行熊经,則最好不要放在Lambdas里冀痕。
- 只有在創(chuàng)建 functional interfaces 的實(shí)例時(shí)弟胀,才建議使用Lambdas
- Lambdas 和 匿名類一樣,你不應(yīng)該去序列化以及反序列化它們眉厨,如果你想序列化或反序列化一個(gè)function object锌奴,則使用一個(gè) private static nested class
什么時(shí)候只能使用匿名類,而不能使用lambdas
- lambdas只限于functional interfaces憾股,如果想創(chuàng)建含有多個(gè)抽象方法的實(shí)例鹿蜀,則要使用匿名類
- lambda不能獲取對(duì)自己的引用,在lambdas中服球, this關(guān)鍵字指的是enclosing instance茴恰,在匿名類中,this關(guān)鍵字指的是anonymous class instance有咨。
為什么優(yōu)先使用method references琐簇,而不是lambdas
- 舉個(gè)java8 Map接口中的merge方法的例子,第一行是使用lambdas座享,第二行是使用method references
map.merge(key, 1, (count, incr) -> count + incr);
map.merge(key, 1, Integer::sum);
- method references 往往能生成更簡潔清晰的代碼婉商,如果一個(gè)lambdas的代碼片段太長或太復(fù)雜,可以把這段代碼抽出來生成一個(gè)新方法渣叛,可以對(duì)該新方法起個(gè)好名字丈秩,然后把lambdas替換成對(duì)該新方法的引用
- method 和 lambda在同一個(gè)類中時(shí),或者使用Function接口中的identity方法淳衙,此時(shí)應(yīng)該優(yōu)先使用lambda蘑秽,如下:
service.execute(GoshThisClassNameIsHumongous::action);
等價(jià)于
service.execute(() -> action());
相似地,
Function.identity()
等價(jià)于
x -> x
有哪幾種方法引用箫攀?
- Static引用
Integer::parseInt
對(duì)應(yīng)的Lambda
str -> Integer.parseInt(str)
- Bound引用
Instant.now()::isAfter
對(duì)應(yīng)的Lambda
Instant then = Instant.now();
t -> then.isAfter(t)
- Unbound引用
String::toLowerCase
對(duì)應(yīng)的Lambda
str -> str.toLowerCase()
- Class Constructor引用
TreeMap<K,V>::new
對(duì)應(yīng)的Lambda
() -> new TreeMap<K,V>
- Array Constructor 引用
int[]::new
對(duì)應(yīng)的Lambda
len -> new int[len]
優(yōu)先使用 standard functional interfaces, 說說你對(duì)java.util.function.Function中的函數(shù)接口的了解
- java.util.Function中一共有43個(gè)函數(shù)接口肠牲,可以概括成6種基本函數(shù)接口,從這6種基礎(chǔ)接口靴跛,可以推導(dǎo)出其他接口缀雳,同時(shí)每一種基本接口,都有對(duì)原始類型int, long 和 double的變體梢睛,6種基本函數(shù)接口如下:
Interface | Function Signature | Example |
---|---|---|
UnaryOperator<T> | T apply(T t) | String::toLowerCase |
BinaryOperator<T> | T apply(T t1, T t2) | BigInteger::add |
Predicate<T> | boolean test(T t) | Collection::isEmpty |
Function<T,R> | R apply(T t) | Arrays::asList |
Supplier<T> | T get() | Instant::now |
Consumer<T> | void accept(T t) | System.out::println |
在使用Streams API時(shí)肥印,應(yīng)該注意什么?
- 使用流绝葡,是為了讓程序變得更shorter and clearer深碱,如果流使你的程序在簡潔的基礎(chǔ)上,又便于閱讀和維護(hù)藏畅,那么就去用它敷硅,否則就遠(yuǎn)離它
- 濫用流,會(huì)讓程序變得難以閱讀和維護(hù)
- 用 流 替換 loop 時(shí),也要注意代碼的可讀性和維護(hù)性的問題
- forEach函數(shù)應(yīng)該僅僅用于report流計(jì)算的結(jié)果竞膳,而不應(yīng)該執(zhí)行計(jì)算
在流中航瞭,如何使用無副作用的函數(shù)
- 不要使用下面的方式實(shí)例化frequency table
Map<String, Long> freq = new HashMap<>();
try (Stream<String> words = new Scanner(file).tokens()) {
words.forEach(word -> {
freq.merge(word.toLowerCase(), 1L, Long::sum);
});
}
- 應(yīng)該使用下面的方式實(shí)例化frequency table
Map<String, Long> freq;
try (Stream<String> words = new Scanner(file).tokens()) {
freq = words
.collect(groupingBy(String::toLowerCase, counting()));
}
- 應(yīng)該使用下面方式獲取 top10 words from a frequency table
List<String> topTen = freq.keySet().stream()
.sorted(comparing(freq::get).reversed())
.limit(10)
.collect(toList());
- 使用 toMap collector 來構(gòu)建一個(gè)<string, enum> 的 map
private static final Map<String, Operation> stringToEnum =
Stream.of(values()).collect(
toMap(Object::toString, e -> e));
- 使用 toMap collector 生成針對(duì)key的特定的value的map
Map<Artist, Album> topHits = albums.collect(
toMap(Album::artist, a->a, maxBy(comparing(Album::sales))));
Collector to impose last-write-wins policy
toMap(keyMapper, valueMapper, (v1, v2) -> v2)
- groupingBy的簡單使用
words.collect(groupingBy(word -> alphabetize(word)))
Map<String, Long> freq = words
.collect(groupingBy(String::toLowerCase, counting()));
- 多關(guān)注 java.util.stream.Collectors中的靜態(tài)方法
為了同時(shí)提供使用 iteration 和 stream 的功能诫硕,應(yīng)該優(yōu)先使用Collection作為返回類型坦辟,而不是Stream
謹(jǐn)慎使用streams parallel,在使用時(shí)應(yīng)該注意的問題
- 現(xiàn)在的Java章办,寫concurrent programs 越來越簡單锉走,但寫出 correct 以及 fast 的 concurrent programs 依舊很難
- 不要盲目的 parallelize stream pipelines
- 如果流的源頭來自Stream.iterate或者在中間操作中使用limit,那么parallelizing a pipeline 不會(huì)提高性能
- 如果流的源頭是 ArrayList, HashMap, HashSet, ConcurrentHashMap instances藕届;arrays; int ranges; long ranges挪蹭,那么parallelizing a pipeline 可能會(huì)提高性能
- parallelizing a stream 不但很可能造成差的性能,它還可能導(dǎo)致不正確的結(jié)果和不可預(yù)測的行為
- 總之休偶,不要輕易 parallelize a stream pipeline