最近抽空看了《Java8实战这本书》,收获很多,这本书着重介绍了Java8的两个新特性:Lambda表达式和stream()的使用,简化了我们的开发。下面是我在读这本书是所做的笔记,也是我的一些收获。
第一段代码
对苹果按重量排序
1 2 3 4 5 6 7 8 9 10 11
| Collections.sort(inventory, new Comparator<Apple>() { public int compare(Apple a1, Apple a2){ return a1.getWeight().compareTo(a2.getWeight()); } }); inventory.sort(comparing(Apple::getWeight)); Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
|
筛选金额较高的交易
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>(); for (Transaction transaction : transactions) { if(transaction.getPrice() > 1000){ Currency currency = transaction.getCurrency(); List<Transaction> transactionsForCurrency = transactionsByCurrencies.get(currency); if (transactionsForCurrency == null) { transactionsForCurrency = new ArrayList<>(); transactionsByCurrencies.put(currency,transactionsForCurrency); } transactionsForCurrency.add(transaction); } } import static java.util.stream.Collectors.toList; Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream().filter((Transaction t) -> t.getPrice() > 1000).collect(groupingBy(Transaction::getCurrency));
|
函数式接口
只定义了一个方法的接口,例如:
1 2 3 4 5 6 7 8 9
| public interface Predicate<T>{ boolean test (T t); } public interface Comparator<T> { int compare(T o1, T o2); }
|
函数时接口的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @FunctionalInterface public interface Predicate<T>{ boolean test(T t); } public static <T> List<T> filter(List<T> list, Predicate<T> p) { List<T> results = new ArrayList<>(); for(T s: list){ if(p.test(s)){ results.add(s); } } return results; } Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty(); List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate); List<String> nonEmpty = filter(listOfStrings, (String s) -> !s.isEmpty());
|
Java8中forEach方法的使用
假如有一个list
集合,循环获取里面的值,Java8
之前是这样做的。
1 2 3 4 5 6 7 8 9 10
| for (int i:list) { System.out.println("Iterator Value::"+i); } Iterator<Integer> it = list.iterator(); while (it.hasNext()){ System.out.println("Iterator Value::"+it.next()); }
|
Java8
后有一个forEach的方法,配合Lambda表达式。简直不要更简单。
1 2 3
| list.forEach(a -> { System.out.println("Iterator Value::"+ a); });
|
Java8中的default关键字
用于在接口中扩充方法,而不影响子接口,或子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public interface DefaultTest { default void foo(){ System.out.println("Calling A.foo()"); } } public class DefaultTestImpl implements DefaultTest { public static void main(String[] args){ DefaultTestImpl defaultTest = new DefaultTestImpl(); defaultTest.foo(); } }
|
Lambda表达式及函数时接口的例子
T -> R |
Function,将类型T的对象转换为类型R的对象 R apply(T t) |
(int, int)->int |
IntBinaryOperator具有唯一一个抽象方法,叫作applyAsInt int applyAsInt(int left, int right |
T->void |
Consumer具有唯一一个抽象方法叫作accept void accept(T t) |
()->T |
Supplier具有唯一一个抽象方法叫作get T get() |
(T, U)->R |
BiFunction具有唯一一个抽象方法叫作apply R apply(T t,) |
Lambda表达式类型检查过程示例
注意特殊的兼容规则
如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当
然需要参数列表也兼容)。例如,以下两行都是合法的,尽管List的add方法返回了一个
boolean,而不是Consumer上下文(T -> void)所要求的void:
1 2 3 4
| Predicate<String> p = s -> list.add(s); Consumer<String> b = s -> list.add(s);
|
方法引用
类似Lambda表达式,但比Lambda表达式更直观,简洁
1 2 3 4 5
| inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())); inventory.sort(comparing(Apple::getWeight));
|
Lambda表达式及其等效方法引用
Lambda |
等效的方法引用 |
(Apple a) -> a.getWeight() |
Apple::getWeight |
() -> Thread.currentThread().dumpStack() |
Thread.currentThread()::dumpStack |
(str, i) -> str.substring(i) |
String::substring |
(String s) -> System.out.println(s) |
System.out::println |
快速创建list集合
1
| List<Integer> weights = Arrays.asList(7,3,4,10);
|
Java8 stream学习
代码举例
假设我现在要获取卡路里小于400的食物,并将这些食物排序
1 2 3 4 5 6 7 8 9
| public static void main(String[] args){ getLowCaloricDishesNamesInJava8(Dish.menu).forEach(System.out::println); } public static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes){ return dishes.stream().filter(d -> d.getCalories() < 400).sorted(Comparator.comparing(Dish::getCalories)) .map(Dish::getName).collect(Collectors.toList()); }
|
stream流的中间操作和终端操作
如上图,流是有数据连(如集合),中间操作链(形成流的一条流水线),终端操作(生成结果)。其中,中间操作的返回结果类型为:Stream<T>
。
流的总结
- 流是“从支持数据处理操作的源生成的一系列元素”。
- 流利用内部迭代:迭代通过filter、map、sorted等操作被抽象掉了。
- 流操作有两类:中间操作和终端操作。
- filter和map等中间操作会返回一个流,并可以链接在一起。可以用它们来设置一条流
水线,但并不会生成任何结果。
- forEach和count等终端操作会返回一个非流的值,并处理流水线以返回结果。
- 流中的元素是按需计算的。
将字符串列表转成字母列表
代码如下:
1 2 3 4 5 6
| List<String> title = Arrays.asList("Java8", "In", "Action"); List<Integer> wordLengths = title.stream().map(String::length).collect(toList()); List<String> collect = title.stream().map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(toList()); System.out.println(collect);
|
执行过程如图:
Lambda表达式打印数组类型的集合
1 2 3 4 5
| List<Integer> numbers1 = Arrays.asList(1, 2, 3); List<Integer> numbers2 = Arrays.asList(3, 4); List<int[]> pairs = numbers1.stream().flatMap(i -> numbers2.stream().map(j -> new int[]{i, j})).collect(toList()); pairs.forEach(pair -> System.out.println("("+pair[0]+","+pair[1]+")"));
|
由于本书才看了一半,后续的笔记还在记录当中。