Java 8高级特性以及在项目中实战

概要

Java 8带来了大量的特性,首当其冲的就是Lambda, Streaming API。我从内容由点到线,由线到面,系统介绍Java 8高级特性。希望通过该分享能为大家带来编程上的帮助。

新特性

  • Lambda
  • Method Reference
  • Type Inference
  • InterfaceFunction
  • Higher-order Function
  • Stream
  • Collectors
  • Optional

特性

Lambda

可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它 有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

哪里可以使用Lambda:

  • 方法体内
  • 方法入参
  • 方法返回值

看一个最简单的Lambda:

@Test
public void test() {
    Consumer<String> consumer = (x) -> log.info("有入参无返回值:{}", x);
    consumer.accept("Percy");
}
接口参数返回类型描述
PredicateTboolean判断
ConsumerTvoid处理一个参数
Function<T,R>TR转换T到R
SupplierNoneT工厂方法
BinaryOperator(T, T)T处理两个参数返回一个相同的参数,求和
总结

** 可以使用变量的地方都可以使用Lambda **

Method Reference

方法引用 :: 使用连续两个双引号进行表示。方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。

类型 示例
引用静态方法 ContainingClass::staticMethodName
引用某个对象的实例方法 containingObject::instanceMethodName
引用某个类型的任意对象的实例方法 ContainingType::methodName
引用构造方法 ClassName::new

类型示例
引用静态方法ContainingClass::staticMethodName
引用某个对象的实例方法containingObject::instanceMethodName
引用某个类型的任意对象的实例方法ContainingType::methodName
引用构造方法ClassName::new
List<User> users = new ArrayList<>();
User u = new User();
u.setName("Zhangsan");
users.add(u);

users.stream()
        .map(User::getName)
        .collect(Collectors.toList());

Collections.sort(users, Comparator.comparing(User::getAge));

https://www.cnblogs.com/JohnTsai/p/5806194.html

Type Inference

Java 8改进了类型推断,在Java 7中使用简单的推断变量。例如

List<String> contents = new ArrayList<>();
contents.add("1");
log.info("简单的类型推断:{}", contents);

上述代码中在ArrayList<>并没有指定类型,由编译器进行推断。

Java 8类型推断增强:

Runnable runnable = () -> {
  log.info("推断使用Runnable, 无需返回结果");
};

Callable<String> callable = () -> {
    log.info("推断使用Runnable, 需要返回结果");
  return "返回结果";
};

ExecutorService es = Executors.newSingleThreadExecutor();
es.submit(runnable);
es.submit(callable);

Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的,接着看下一个示例:

// 入参是一个参数的
Predicate<Integer> predicate = (x) -> x > 30;
// 可以转换对象,入参T, 返回R
Function<Integer, User> converter = (it) -> {
    User u = new User();
    u.setAge(it);
    u.setName("Percy");
    return u;
};

List<Integer> ages = new ArrayList<>();
ages.add(22);
ages.add(28);
ages.add(31);
ages.add(32);
ages.add(33);
ages.add(34);
ages.add(24);
ages.add(37);

List<User> users = ages.stream()
        .filter(predicate)
        .map(converter)
        .collect(Collectors.toList());

两个参数的类型推断:

// 两个参数的推断
BinaryOperator<Long> add = (x, y) -> x + y;
Long addResult = add.apply(3L, 5L);

log.info("addResult结果:{}", addResult);

Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
Integer r = comparator.compare(3, 1);
log.info("参数比较大小结果:{}", r);
总结

上述代码的两个Lambda函数都是一个入参,从编译器来说根本不关心出参概念。只要是一个入参就认为是相同的,所以编译器可以清晰的推断出返回的类型函数。

Higher-order Function

高阶函数:

函数编程,函数是第一公民。高阶函数就是接受至少一个函数作为参数或者返回的结果是一个函数的函数。

  • 至少一个函数作为参数
  • 返回的结果是一个函数
public Function<String, User> getFunc(Integer age) {
    log.info("可以根据不同入参进行逻辑判断,返回不同实现的函数:{}", age);
    Function<String, User> func = (name) -> {
        User user = new User();
        user.setAge(30);
        user.setName(name);
        return user;
    };
    return func;
}

@Test
public void testHigherOrderFunction() {
    List<String> names = new ArrayList<>();
    names.add("some");
    Function<String, User> func = getFunc(20);
    List<User> users = names.parallelStream()
            .map(func)
            .collect(Collectors.toList());
    log.info("users:{}", users);
}

Stream

在这里插入图片描述

生成流
中间操作
操 作类 型返回类型操作参数函数描述符
filter中间StreamPredicateT -> boolean
map中间StreamFunction<T, R>T -> R
limit中间StreamPredicateT -> boolean
sorted中间StreamComparator(T, T) -> int
distinct中间StreamPredicateT -> boolean
终端操作
操 作类 型返回类型
forEach终端消费流中的每个元素并对其应用 Lambda。这一操作返回 void
count终端返回流中元素的个数。这一操作返回 long
collect终端把流归约成一个集合,比如 List、Map 甚至是 Integer
parallel()

并行化

List<Integer> ints = Arrays.asList(3, 45, 53, 342, 2, 23, 44);
ints.stream().parallel().forEach(it -> {
    log.info("===:{}", it);
});

输出的日志:

2019/03/23 16:24:19.916 ForkJoinPool.commonPool-worker-1 [INFO] LambdaTest (LambdaTest.java:132) ===:45
2019/03/23 16:24:19.916 ForkJoinPool.commonPool-worker-2 [INFO] LambdaTest (LambdaTest.java:132) ===:3
2019/03/23 16:24:19.916 main [INFO] LambdaTest (LambdaTest.java:132) ===:2
2019/03/23 16:24:19.916 ForkJoinPool.commonPool-worker-6 [INFO] LambdaTest (LambdaTest.java:132) ===:23
2019/03/23 16:24:19.916 ForkJoinPool.commonPool-worker-4 [INFO] LambdaTest (LambdaTest.java:132) ===:342
2019/03/23 16:24:19.916 ForkJoinPool.commonPool-worker-5 [INFO] LambdaTest (LambdaTest.java:132) ===:53
2019/03/23 16:24:19.916 ForkJoinPool.commonPool-worker-3 [INFO] LambdaTest (LambdaTest.java:132) ===:44

1、并行化以后不是按照特性顺序,随机化。
2、内部使用的是ForkJoinPool简化多线程操作。
3、如果程序不是必须按照特性顺序可以使用并行流,读取数据库或调用下游服务。

Collectors

收集器用作高级归约
count()
min()
max()
joining()
reducing()

BinaryOperator<Integer> aa = (x, y) -> x + y;
Integer a1 = ints.stream().reduce(Integer::sum).get();
Integer a2 = ints.stream().collect(Collectors.reducing(Integer::sum)).get();
Integer a3 = ints.stream().collect(Collectors.reducing(0, x -> x, Integer::sum));
log.info("========{}, {}, {}", a1, a2, a3);
groupingBy
Map<Integer, Long> countMapping = users.stream()
        .collect(Collectors.groupingBy(User::getAge, Collectors.counting()));

log.info("==========={}", countMapping);
// {25=2, 27=1, 28=1, 30=2}

通常有时候我们需要把一个数据库的List转换为Map结构,例如key为主键id:

// 初始化数据
List<User> users = new ArrayList<>();
User u = new User();
u.setId(1L);
u.setAge(30);
u.setName("Zhangsan301");
users.add(u);

u = new User();
u.setId(2L);
u.setAge(25);
u.setName("Zhangsan251");
users.add(u);

u = new User();
u.setId(3L);
u.setAge(25);
u.setName("Zhangsan252");
users.add(u);

u = new User();
u.setId(4L);
u.setAge(27);
u.setName("Zhangsan271");
users.add(u);

u = new User();
u.setId(5L);
u.setAge(28);
u.setName("Zhangsan281");
users.add(u);

u = new User();
u.setId(6L);
u.setAge(30);
u.setName("Zhangsan302");
users.add(u);

// List -> Map
Map<Long, User>  userIdMapping = users.stream()
        .collect(Collectors.toMap(User::getId, it -> it, (x, y) -> x));
log.info("===========userIdMapping{}", userIdMapping);

输出结果:

===========userIdMapping{1=User(id=1, age=30, name=Zhangsan301), 2=User(id=2, age=25, name=Zhangsan251), 3=User(id=3, age=25, name=Zhangsan252), 4=User(id=4, age=27, name=Zhangsan271), 5=User(id=5, age=28, name=Zhangsan281), 6=User(id=6, age=30, name=Zhangsan302)}

是不是很便利,之前可能还要循环一次,现在一行就可以解决了问题。

partitioningBy

这个用途不多,先忽略

自定义收集器

为了适应自己特殊业务可以自定义收集器。

Optional

总结

Java8的核心功能就是Lambda和Streaming API,本次分享着重讲解了核心的语法糖Lambda.只要可以使用变量参数的地方都可以使用Lambda, Lambda内部要求无状态化,就是final类型的,不能变更状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值