抛砖引玉!写出更简洁优美的代码
- 自java5以来最大的版本变动
- 很大程度增强java类库
- 主要目标
- 更高的开发效率
- 更高代码可用性
- 更好的利用多核和多处理器系统
- 函数式接口:只包含一个方法的接口
- 语法:
(参数) -> 表达式
或者(参数) -> { 语句; }
- 方法引用
- 跟Lambda表达式一样,语法:
对象引用::方法名
示例1
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是线程");
}
}).start();
new Thread(() -> {
System.out.println("我是Lambda创建的线程");
}).start();
示例2 接口的匿名实现类全部可以使用Lambda表达式声明(单个方法)
public interface Radio {
void play();
}
// java8以前的实现
Radio radio = new Radio() {
@Override
public void play() {
System.out.println("播放广播");
}
};
// java8的实现
Radio radio2 = () -> System.out.println("播放广播");
在Stream API中会有大量使用
List
// 模拟数据
List<Person> persons = Lists.newArrayList(new Person("张三"), new Person("李四"));
// 以前的方式
for (Person person : persons) {
System.out.println(person.getName());
}
// 新的方式
persons.forEach(person -> System.out.println(person.getName()));
Map
// 模拟数据
Map<String, Person> persons = ImmutableMap.of("张三", new Person("张三", 20), "李四", new Person("李四", 22));
// 以前的方式
for (String key : persons.keySet()) {
System.out.println(key + ":" + persons.get(key).getAge());
}
// 新的方式
persons.forEach((key, person) -> System.out.println(key + ":" + person.getAge()));
Set
// 模拟数据
Set<Person> persons = ImmutableSet.of(new Person("张三", 20), new Person("李四", 22));
// 以前的方式
for (Person person : persons) {
System.out.println(person.getName());
}
// 新的方式
persons.forEach(person -> System.out.println(person.getName()));
点开源码看下,以List为例
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
底层还是原先的迭代方式,但是语义上更好理解了。
感兴趣的可以打开源码,看看java.util.function包下面的这些函数接口,在很多地方都有使用。
流(Stream)仅仅代表着数据流,并没有数据结构,所以他遍历完一次之后便再也无法遍历(这点在编程时候需要注意,不像Collection,遍历多少次里面都还有数据),它的来源可以是Collection、array、io等等。
流作用是提供了一种操作大数据接口,让数据操作更容易和更快。它具有过滤、映射以及减少遍历数等方法,这些方法分两种:中间方法和终端方法,“流”抽象天生就该是持续的,中间方法永远返回的是Stream,因此如果我们要获取最终结果的话,必须使用终点操作才能收集流产生的最终结果。区分这两个方法是看他的返回值,如果是Stream则是中间方法,否则是终点方法。有点类似sql语句的语义,自行脑补,哈哈。
以下列举几个常用的方法,更多使用方法请自行查阅API文档
从集合总过滤数据
List<Person> persons = Lists.newArrayList(new Person("张三", 20), new Person("赵六", 20), new Person("李四", 22), new Person("王五", 18));
// 过滤出年龄大于18岁的数据,转换List
List<Person> filteredPersons = persons.stream().filter(person -> person.getAge() > 18).collect(Collectors.toList());
System.out.println(JSON.toJSONString(filteredPersons));
// 过滤出年龄大于18岁的数据,转换Set
Set<Person> filteredPersonSet = persons.stream().filter(person -> person.getAge() > 18).collect(Collectors.toSet());
System.out.println(JSON.toJSONString(filteredPersonSet));
用来转换对象,比如把集合里面的某些属性组合成一个集合
List<Person> persons = Lists.newArrayList(new Person("张三", 20), new Person("赵六", 20), new Person("李四", 22), new Person("王五", 18));
List<String> names = persons.stream().map(Person::getName).collect(Collectors.toList());
System.out.println(JSON.toJSONString(names));
count是一个流的终点方法,用来统计
List<Person> persons = Lists.newArrayList(new Person("张三", 20), new Person("赵六", 20), new Person("李四", 22), new Person("王五", 18));
long count = persons.stream().map(Person::getName).count();
collect方法也是一个流的终点方法,可收集最终的结果。
List<Person> persons = Lists.newArrayList(new Person("张三", 20), new Person("赵六", 20), new Person("李四", 22), new Person("王五", 18));
// 过滤出年龄大于18岁的数据,转换List
List<Person> filteredPersons = persons.stream().filter(person -> person.getAge() > 18).collect(Collectors.toList());
System.out.println(JSON.toJSONString(filteredPersons));
// 过滤出年龄大于18岁的数据,转换Set
Set<Person> filteredPersonSet = persons.stream().filter(person -> person.getAge() > 18).collect(Collectors.toSet());
System.out.println(JSON.toJSONString(filteredPersonSet));
或者使用特定的实现类收集结果
List<Person> filteredPersons = persons.stream().filter(person -> person.getAge() > 18).collect(Collectors.toCollection(ArrayList::new));
System.out.println(JSON.toJSONString(filteredPersons));
或者对结果分组
List<Person> persons = Lists.newArrayList(new Person("张三", 20, "男"), new Person("赵六", 20, "男"), new Person("李四", 22, "男"), new Person("王五", 18, "女"));
// 按照年龄分组
Map<Integer, List<Person>> groupMap = persons.stream().collect(Collectors.groupingBy(Person::getAge));
System.out.println(JSON.toJSONString(groupMap));
// 按年龄统计名字
Map<Integer, List<String>> groupName = persons.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.mapping(Person::getName, Collectors.toList())));
System.out.println(JSON.toJSONString(groupName));
// 按性别求年龄总和
Map<String, Integer> groupAgeCount = persons.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.reducing(0, Person::getAge, Integer::sum)));
System.out.println(JSON.toJSONString(groupAgeCount));
// 统计各年龄的数量
Map<Integer, Integer> groupCount = persons.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.summingInt(p -> 1)));
System.out.println(JSON.toJSONString(groupCount));
find结合filter使用也是很常见的
List<Person> persons = Lists.newArrayList(new Person("张三", 20), new Person("赵六", 20), new Person("李四", 22), new Person("王五", 18));
// 查找一个年龄大于18岁的
Optional<Person> optional = persons.stream().filter(person -> person.getAge() > 18).findFirst();
if (optional.isPresent()) {
System.out.println(JSON.toJSONString(optional.get()));
}
需要注意,stream的操作都是管道特性,通过一个例子可以看出
List<Person> persons = Lists.newArrayList(new Person("张三", 20, "男"), new Person("赵六", 20, "男"), new Person("李四", 22, "男"), new Person("王五", 18, "女"));
persons.stream()
.filter(p -> {
System.out.println("filter=>" + p);
return true;
})
.map(p -> {
System.out.println("map=>" + p.getName());
return p.getName();
})
.forEach(p -> System.out.println("结果=>" + p));
结果如下:
filter=>example.model.Person@3a4afd8d
map=>张三
结果=>张三
filter=>example.model.Person@1996cd68
map=>赵六
结果=>赵六
filter=>example.model.Person@3339ad8e
map=>李四
结果=>李四
filter=>example.model.Person@555590
map=>王五
结果=>王五
更多参考:http://colobu.com/2014/11/18/Java-8-Stream/
每个Stream都有两种模式:顺序执行和并行执行。 顺序流
List<Person> persons = Lists.newArrayList(new Person("张三"), new Person("李四"));
persons.stream().forEach(System.out::print);
并行流
List<Person> persons = Lists.newArrayList(new Person("张三"), new Person("李四"));
persons.parallelStream().forEach(System.out::print);
并行流的原理是将数据拆分成多个段,然后并行执行,然后将结果合并到一起返回。
以前我们需要自己实现并行,现在使用java8就是so easy了!
注意点: 小数据量时候没必要使用并行流,比如几条数据。 建议在数据库批量操作、HTTP批量请求API时候使用并行操作。
案例: 并行修改300w数据,把数据库拖到高负载了- -! 调用百度地图API,给一些带地址的数据查询坐标,节省70%的时间
更多场景请结合业务组合使用
API操作流程如下
Java一直缺少BASE64编码 API,以至于通常在项目开发中会选用第三方的API实现。但是,Java 8实现了BASE64编解码API,它包含到java.util包。
java.util.Base64工具类提供了一套静态方法获取下面三种BASE64编解码器
- Basic编码
- URL编码
- MIME编码
Basic编码是标准的BASE64编码,用于处理常规的需求:输出的内容不添加换行符,而且输出的内容由字母加数字组成。
基本用法
// 编码
Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8))
// 解码
new String(Base64.getDecoder().decode(text), StandardCharsets.UTF_8)
编码URL 默认的编码可能会出现“/”,这个在URL里面会有特殊语义,所以使用URL编码器
Base64.getUrlEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8))
网上搞到的性能测试
参考:http://my.oschina.net/benhaile/blog/267738
Java8彻底删除了永久代,取而代之的是“元空间”
- 它是本地堆内存中的一部分
- 它可以通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize来进行调整
- 当到达XX:MetaspaceSize所指定的阈值后会开始进行清理该区域
- 如果本地空间的内存用尽了会收到java.lang.OutOfMemoryError: Metadata space的错误信息。
- 和持久代相关的JVM参数-XX:PermSize及-XX:MaxPermSize将会被忽略掉。
G1垃圾回收器优化
http://ifeve.com/java-garbage-first/
鉴于大家对joda-time使用的比较熟悉了,新的时间API自行查阅
http://ifeve.com/20-examples-of-date-and-time-api-from-java8/
http://ifeve.com/java-se-8-concurrent-tool-enhance/
Java8特性 http://ifeve.com/java-8-features-tutorial/
Java 8学习资料汇总 http://ifeve.com/java8-learning-resources/
Java 8 Lambda表达式和功能接口示例教程 http://www.journaldev.com/2763/java-8-lambda-expressions-and-functional-interfaces-example-tutorial
Java Stream API示例教程 http://www.journaldev.com/2774/java-8-stream-api-example-tutorial
http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
Stream语法详解 http://ifeve.com/stream/
reduction http://docs.oracle.com/javase/tutorial/collections/streams/reduction.html