參考文章
http://lucida.me/blog/java-8-lambdas-insideout-language-features/
http://www.jb51.net/article/48304.htm
JDK1.8包含了很多內(nèi)建的函數(shù)式接口镀裤,在老Java中常用到的比如Comparator或者Runnable接口握牧,這些接口都增加了@FunctionalInterface注解以便能用在lambda上怀浆。
- Predicate接口
Predicate 接口只有一個(gè)參數(shù)本橙,返回boolean類型磕谅。該接口包含多種默認(rèn)方法來(lái)將Predicate組合成其他復(fù)雜的邏輯(比如:與狐蜕,或骏掀,非):
Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo"); // true
predicate.negate().test("foo"); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
- Comparator
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");
comparator.compare(p1, p2); // > 0
comparator.reversed().compare(p1, p2); // < 0
- Function 接口
Function 接口有一個(gè)參數(shù)并且返回一個(gè)結(jié)果鸠澈,并附帶了一些可以和其他函數(shù)組合的默認(rèn)方法(compose, andThen):
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
backToString.apply("123"); // "123"
- Supplier 接口
Supplier 接口返回一個(gè)任意范型的值乔夯,和Function接口不同的是該接口沒(méi)有任何參數(shù)
Supplier<Person> personSupplier = Person::new;
personSupplier.get(); // new Person
- Consumer 接口
Consumer 接口表示執(zhí)行在單個(gè)參數(shù)上的操作。
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
- Optional 接口
Optional 不是函數(shù)是接口款侵,這是個(gè)用來(lái)防止NullPointerException異常的輔助類型末荐,這是下一屆中將要用到的重要概念,現(xiàn)在先簡(jiǎn)單的看看這個(gè)接口能干什么:
Optional 被定義為一個(gè)簡(jiǎn)單的容器新锈,其值可能是null或者不是null甲脏。在Java 8之前一般某個(gè)函數(shù)應(yīng)該返回非空對(duì)象但是偶爾卻可能返回了null,而在Java 8中妹笆,不推薦你返回null而是返回Optional块请。
Optional<String> optional = Optional.of("bam");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("fallback"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
- Stream 接口
Stream 接口
java.util.Stream 表示能應(yīng)用在一組元素上一次執(zhí)行的操作序列。Stream 操作分為中間操作或者最終操作兩種拳缠,最終操作返回一特定類型的計(jì)算結(jié)果墩新,而中間操作返回Stream本身,這樣你就可以將多個(gè)操作依次串起來(lái)窟坐。Stream 的創(chuàng)建需要指定一個(gè)數(shù)據(jù)源海渊,比如 java.util.Collection的子類,List或者Set哲鸳, Map不支持臣疑。Stream的操作可以串行執(zhí)行或者并行執(zhí)行
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
- Filter 過(guò)濾
過(guò)濾通過(guò)一個(gè)predicate接口來(lái)過(guò)濾并只保留符合條件的元素,該操作屬于中間操作徙菠,所以我們可以在過(guò)濾后的結(jié)果來(lái)應(yīng)用其他Stream操作(比如forEach)讯沈。forEach需要一個(gè)函數(shù)來(lái)對(duì)過(guò)濾后的元素依次執(zhí)行。forEach是一個(gè)最終操作婿奔,所以我們不能在forEach之后來(lái)執(zhí)行其他Stream操作缺狠。
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa2", "aaa1"
- Sort 排序
排序是一個(gè)中間操作,返回的是排序好后的Stream萍摊。如果你不指定一個(gè)自定義的Comparator則會(huì)使用默認(rèn)排序
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa1", "aaa2"
需要注意的是挤茄,排序只創(chuàng)建了一個(gè)排列好后的Stream,而不會(huì)影響原有的數(shù)據(jù)源记餐,排序之后原數(shù)據(jù)stringCollection是不會(huì)被修改的
- Map 映射
中間操作map會(huì)將元素根據(jù)指定的Function接口來(lái)依次將元素轉(zhuǎn)成另外的對(duì)象驮樊,下面的示例展示了將字符串轉(zhuǎn)換為大寫字符串。你也可以通過(guò)map來(lái)講對(duì)象轉(zhuǎn)換成其他類型片酝,map返回的Stream類型是根據(jù)你map傳遞進(jìn)去的函數(shù)的返回值決定的囚衔。
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
- Match匹配
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
- Count 計(jì)數(shù)
計(jì)數(shù)是一個(gè)最終操作,返回Stream中元素的個(gè)數(shù)雕沿,返回值類型是long练湿。
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b"))
.count();
System.out.println(startsWithB); // 3
- Reduce 規(guī)約
這是一個(gè)最終操作,允許通過(guò)指定的函數(shù)來(lái)講stream中的多個(gè)元素規(guī)約為一個(gè)元素审轮,規(guī)越后的結(jié)果是通過(guò)Optional接口表示的:
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
- 并行Streams
前面提到過(guò)Stream有串行和并行兩種肥哎,串行Stream上的操作是在一個(gè)線程中依次完成辽俗,而并行Stream則是在多個(gè)線程上同時(shí)執(zhí)行。
下面的例子展示了是如何通過(guò)并行Stream來(lái)提升性能:
首先我們創(chuàng)建一個(gè)沒(méi)有重復(fù)元素的大表:
int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
然后我們計(jì)算一下排序這個(gè)Stream要耗時(shí)多久篡诽,
串行排序:
long t0 = System.nanoTime();
long count = values.stream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));
// 串行耗時(shí): 899 ms
并行排序
long t0 = System.nanoTime();
long count = values.parallelStream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("parallel sort took: %d ms", millis));
// 并行排序耗時(shí): 472 ms
上面兩個(gè)代碼幾乎是一樣的崖飘,但是并行版的快了50%之多,唯一需要做的改動(dòng)就是將stream()改為parallelStream()杈女。
- Map
前面提到過(guò)朱浴,Map類型不支持stream,不過(guò)Map提供了一些新的有用的方法來(lái)處理一些日常任務(wù)达椰。
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
map.putIfAbsent(i, "val" + i);
}
map.forEach((id, val) -> System.out.println(val));
以上代碼很容易理解翰蠢, putIfAbsent 不需要我們做額外的存在性檢查,而forEach則接收一個(gè)Consumer接口來(lái)對(duì)map里的每一個(gè)鍵值對(duì)進(jìn)行操作啰劲。
下面的例子展示了map上的其他有用的函數(shù)
map.computeIfPresent(3, (num, val) -> val + num);
map.get(3); // val33
map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9); // false
map.computeIfAbsent(23, num -> "val" + num);
map.containsKey(23); // true
map.computeIfAbsent(3, num -> "bam");
map.get(3); // val33
接下來(lái)展示如何在Map里刪除一個(gè)鍵值全都匹配的項(xiàng)
map.remove(3, "val3");
map.get(3); // val33
map.remove(3, "val33");
map.get(3); // null
另外一個(gè)有用的方法:
map.getOrDefault(42, "not found"); // not found
對(duì)Map的元素做合并也變得很容易了:
Merge做的事情是如果鍵名不存在則插入梁沧,否則則對(duì)原鍵對(duì)應(yīng)的值做合并操作并重新插入到map中。
map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9); // val9
map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9); // val9concat