java8 新特性集锦和心得

 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。其中后三种属于工具类直接用即可,就不进行细说了。

二.特性详解

1.Lambda表达式

Lambda表达式是基于新特性包里Stream API的,即位于java 8的java.util.stream包里面,把函数式编程风格引入到java中了。原理是和Callable类似,启动一个新的线程来执行函数体。

基础语法

expression = (variable) -> action

  • variable: 这是一个变量,一个占位符。像x,y,z,可以是多个变量。
  • action: 这里我称它为action, 这是我们实现的代码逻辑部分,它可以是一行代码也可以是一个代码片段

可以看到Java中lambda表达式的格式:参数、箭头、以及动作实现,当一个动作实现无法用一行代码完成,可以编写 一段代码用{}包裹起来。

使用->将参数和实现逻辑分离,当运行这个线程的时候执行的是->之后的代码片段,且编译器帮助我们做了类型推导; 这个代码片段可以是用{}包含的一段逻辑。

也可以包含多个参数,例如:

int sum = (x, y) -> x + y;

 

集合用法:

范例:

List lst = JsonFormatter.toObject(JsonFormatter.toJsonString(responseVo.getData()), List.class);

if (!ListUtil.isEmpty(lst)) {
    list = lst.stream().map(j -> JsonUtil.formatObject(j, clazz)).collect(Collectors.toList());
}


Set numSet = redisUtil.zrangeStr(RedisKey.SA_FMS_MEET_ZSET, startIndx, endIndx - 1);

//Format room number to [6 digits], then sort in [ascending] order
List resList = numSet.stream().map(str -> {
    return String.format("%06d", Integer.valueOf(str));
}).sorted((String s1, String s2) -> {
    return s1.compareTo(s2);
}).collect(Collectors.toList());

//Delete data
redisUtil.delete(RedisKey.SA_ROOM_NO_LIST_ + suffixKey);

//Save data
redisUtil.rightPushAll(RedisKey.SA_ROOM_NO_LIST_ + suffixKey, resList, Constants.CACHE_EXPIRATION_WEEKLY); 
  

 

线程用法:

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> testAmtMapList = testMap.values();
testAmtMapList.forEach(r -> {
testAmtMapList.add(r.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add));
});
BigDecimal totalAmount = testAmtMapList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);

//一般数字求和
Long totalRemainQuantity = testList.stream()
                           .filter(c -> c.getCpId().equals(cpId))
                                                           .mapToLong(TestInfoBo::getRemainQuantity).sum();

排序:

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]

2.函数式接口

λ表达式的类型,叫做 目标类型(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;

3.方法引用

 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);

 

你可能感兴趣的:(Java)