java8问世已经好几年了,但很多程序猿/媛小伙伴们都还没普遍在项目里去使用起来,更多的可能还是缺少了解,究其根源,我想是因为国内没有能普及到程序员层面的活动或大会去推广,目前每年举办的各类活动或大会大都是针对高级架构师级别的,比如极客邦联合InfoQ举办的ArchSummit等等,却很少有针对程序员层面的沙龙。
好了,废话不多说了,这里分享下收集的java8几个重要特性。
这里分享一个网上的新特性全集的框图,先对总览有个了解:
https://www.processon.com/view/5abb31abe4b027675e42cebc
综合来看,在日常项目里比较常用的是基于流的Lambda的使用,本文主要围绕这方面来讲解。大家会问了,这个东西怎么来的呢?
其实,Lambda属于函数式编程,而函数式语法很早就在一些编程语言中如Scala,Ruby中有了,其根本还是基于类型推导和闭包的思想,例如在Scala里,我们用.filter(aa=>aa.contains("hello"))来在集合里查找包含"hello"字符串的记录,而在Java8中的Lambda表达式,允许把函数作为一个方法的参数,使用-> 替代=>并且做了很多增强功能(例如单句执行可省略该符号)。
Stream:流式处理,任何集合类都可以变成流之后进行各种操作和处理,和早期java I/O里的InputStream和OutputStream也是先把一个文件File对象或业务数据Object转为byte放入Stream对象变成二进制流在网络上通信类似的道理。
其他本文涉及到的特性:
方法引用、函数式接口、接口默认方法、Optional类、DateTimeAPI、Nashorn和Js引擎、Base64。其中后三种属于工具类直接用即可,就不进行细说了。
Lambda表达式是基于新特性包里Stream API的,即位于java 8的java.util.stream包里面,把函数式编程风格引入到java中了。原理是和Callable类似,启动一个新的线程来执行函数体。
基础语法:
expression = (variable) -> action
可以看到Java中lambda表达式的格式:参数、箭头、以及动作实现,当一个动作实现无法用一行代码完成,可以编写 一段代码用{}包裹起来。
使用->将参数和实现逻辑分离,当运行这个线程的时候执行的是->之后的代码片段,且编译器帮助我们做了类型推导; 这个代码片段可以是用{}包含的一段逻辑。
也可以包含多个参数,例如:
int sum = (x, y) -> x + y;
集合用法:
范例:
List
线程用法:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
Runnable r = () -> System.out.println("do something.");
其他用法方式:
(params) -> expression
(params) -> statement
(params) -> { statements }
例如:
() -> System.out.println("Hello Lambda Expressions");
条件判断用法:
写了3个Demo参考:
//Demo1-验证条件
import java.util.function.Predicate;
int age = 25;
Predicate agePredicate = x -> x >= 18;
return agePredicate.test(age);
//Demo2-封装成专门判断的子方法
//基于Demo1,我们可以封装条件判断到一个子方法里面(尤其是很多条件的时候,可以用List>),亦或把要校验的属性都放到一个Map里通过K-V某个规则校验
private void doPredicate(Object value, Predicate predicate){
if(value == null){
throw new RuntimeException("No predicate param value";)
}
};
//调用
boolean isAdult = doPredicate(20, x -> x >= 18);
//Demo3-代入Lambda使用
//可结合业务条件进行动态条件嵌入
Predicate filterCondition = null;
if (StringUtils.isNotBlank(orderId)) {
filterCondition = (t) -> null != t && orderId.equals(t.getOrderId());
} else if (StringUtils.isNotBlank(orderItemId)) {
filterCondition = (t) -> null != t && orderItemId.equals(t.getOrderItemId());
}
Map> testMap = [此处省略集合构造]...;
final TestInfoBo testInfoBo = testMap.keySet().stream()
.filter(filterCondition).findFirst().get();
Swing:
show.addActionListener((e) -> {
System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});
List迭代:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
features.forEach(System.out::println); //方法引用
List list = java.util.Arrays.asList("a","b","c");
Stream.iterate(0, i -> i + 1).limit(list.size()).forEach(i -> {
System.out.println(String.valueOf(i) + list.get(i));
});
Stream API条件过滤:
List lst = Arrays.asList("aaeett","bbcc","ccwewff","ddd","ebbbe");
filter(lst, (str) => str.startWith("a"));
filter(languages, (str)->str.endsWith("d"));
filter(languages, (str)->true);
filter(languages, (str)->false);
filter(languages, (str)->str.length() > 4);
public static void filter(List names, Predicate condition){
for(String name:names){
if(condition.test(name)){
System.out.println(name + " ");
}
}
}
或:
List filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
条件合并:
//也可以用and()、or()和xor()逻辑函数来合并Predicate,
//比如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate startsWithJ = (n) -> n.startsWith("J");
Predicate fourLetterLong = (n) -> n.length() == 4;
names.stream().filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print("nName, which starts with 'J' and four letter"));
Map和Reduce的使用:
map: 将集合类(例如列表)元素进行转换
reduce: 折叠操作,可以将所有值合并成一个
//计算总和
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
//连接
List G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
//集合去重
List numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s, Square Without duplicates : %s %n", numbers, distinct);
//集合取值操作
List primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
//排序(自然排序)
List list = Arrays.asList(2, 7, 3, 1, 8, 6, 4);
list.sort(Comparator.naturalOrder());
System.out.println(list);
Map过滤:
Map map = weekMap.entrySet().stream().filter(r -> r.getKey() <= 3)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Map map = weekMap.entrySet().stream().filter(r -> r.getValue().startsWith("S"))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
assertThat(map.values(), contains("Saturday","Sunday"));
Map map = Maps.filterValues(weekMap, r -> r.startsWith("S"));
assertThat(map.values(), contains("Saturday","Sunday"));
ListUtil分组的用法:
if (thresholdsDtos.size() > 0) {
Map> map = ListUtil.groupByProperty(thresholdsDtos, "dayOfWeek",
Integer.class);
List scoreList = new LinkedList();
for (String time : times) {
float scores = 0;
for (Integer key : map.keySet()) {
List dos = map.get(key);
dos = dos.stream().filter(d -> d.getSessionTime().equals(time)).collect(Collectors.toList());
BookingThresholdDto dt = dos.get(0);
scores += dt.getScore();
}
scoreMap.put(time, scores);
scoreList.add(scores);
}
Collections.sort(scoreList);
}
集合内分组操作:
Map> groupCouponMap;
testGroupMap = testInfoList.stream()
.collect(Collectors.groupingBy(TestInfoBo::getTestId));
for (Map.Entry> entry : testGroupMap.entrySet()) {
//...
}
从map中求和:
//金额求和
Map> testMap = new HashMap(16);
List testAmountList = new ArrayList();
if(!testMap.isEmpty()){
Collection
排序:
List ids = Arrays.asList(1, 2, 5, 4, 3);
// 使用lambda表达式
Comparator comparator1 = (a, b) -> a - b;
// 使用静态方法引用
Comparator comparator2 = Integer::compare;
List sorted1 = ids.stream().sorted(comparator1).collect(Collectors.toList());
List sorted2 = ids.stream().sorted(comparator2).collect(Collectors.toList());
System.out.println(ids);// [1, 2, 5, 4, 3]
System.out.println(sorted1); // [1, 2, 3, 4, 5]
System.out.println(sorted2); // [1, 2, 3, 4, 5]
λ表达式的类型,叫做 目标类型(target type)--函数接口(functional interface),只能有一个显式声明的抽象方法,使用@FunctionalInterface标注。
认识心得:lambda其实就是为@FunctionalInterface服务的,基于自动的类型推导机制,能够用一种全新的、简洁的语法创建函数式接口的对象。当然如果一个接口不符合函数式接口的定义,是不能通过lambda表达式来创建其对象的。lambda其实就是定义入参、函数体、返回值,然后可以生成任意一个接口(符合该函数入参和返回值的函数式接口)的对象。
常用的系统自带的函数接口(也可以自定义):
@FunctionalInterface
public interface Runnable { void run(); }
public interface Callable { V call() throws Exception; }
public interface ActionListener { void actionPerformed(ActionEvent e); }
public interface Comparator { int compare(T o1, T o2); boolean equals(Object obj); }
//Demo
@FunctionalInterface
public interface CatInterface {
void sayHello();
}
//调用
CatInterface cat = () -> {System.out.println("cat");}
cat.sayHello();
//其他
void add(int left, int right);
TwoArgsInterface twoArgs1 = (int a, int b) -> System.out.println("no argument");
TwoArgsInterface twoArgs2 = (a, b) -> System.out.println("no argument");
//当参数个数只有一个,可以省略小括号:
OneArgsInterface oneArgs3 = a -> System.out.println("arguments=" + a);
//当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号:
DemoInterface way2 = a -> a * 10;
JDK8中的方法引用分成4类:静态方法引用、实例方法引用、构造方法引用、以静态方式引用实例方法:
静态方法引用
ClassName::staticMethodName:
String::valueOf 等价于lambda表达式 (s) -> String.valueOf(s)
Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y)
//静态方法引用pow
MyInterface ins1 = Math::pow;
System.out.println(ins1.calculate(2, 4) == 16);
//静态方法引用max
MyInterface ins2 = Math::max;
System.out.println(ins2.calculate(2, 4) == 4);
实例方法引用
instanceReference::methodName
str::toString 等价于lambda表达式 () -> str.toString()
str::concat 等价于lambda表达式 (another) -> str.concat(another)
//例:
//比较操作:不用自己实现Comparator
//方式3
Collections.sort(persons, Comparator.comparing(Person::getName));
System.out.println(persons);
//按照默认顺序的相反顺序排序
List descList = students.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println(descList);
//使用lambda表达式创建Function对象
Function extractIdWay1 = (student) -> student.getId();
// 使用方法引用简化lambda
Function extractIdWay2 = Student::getId;
// Comparator.comparing(Function keyExtractor)
Comparator byId = Comparator.comparing(extractIdWay2);
// 升序
List ascList = students.stream().sorted(byId).collect(Collectors.toList());
System.out.println(ascList);
// 降序
List descList = students.stream().sorted(byId.reversed()).collect(Collectors.toList());
System.out.println(descList);