lambda 表达式
当一个接口只有一个方法的时候都可以使用lambda 表达式代替 这种称为函数接口可以用 @FunctionalInterface 修饰
// 使用匿名类的方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
//使用lambda 表达式方式
new Thread(()-> System.out.print("hello") );
-
lambda 表达式多种形式
1) ActionListener listener= event-> System.out.print("hello");
// event表示方法的参数括号 lambda表达式返回可以看成是一个函数的引用
2) BinaryOperator<Long> add = (x,y)->x+y;
// 同样如果有2个参数可以这样表示
3) BinaryOperator<Long> add =(Long x,Long y)-> { x+y; System.out.print("hello"); }
// 同样 我们可以加上具体的参数类型 如果是多行需要加 {}
-
使用lambda表达式实现设计模式
这里我们实现一个解压算法的策略模式
// 定义好接口
@FunctionalInterface
public interface Compression{
public OutputStream compress(OutputStream data) throws IOException;
}
// 定义调用类
class Compressor {
private Compression strategy;
public Compressor(Compression starategy){
this.strategy = starategy;
}
public void compress() throws IOException {
strategy.compress(new FileOutputStream(new File("test.txt")));
}
}
// 传统的做法 定义好实现类 lambda省略这个步骤
class ZipCompression implements Compression{
@Override
public OutputStream compress(OutputStream data) throws IOException {
return new ZipOutputStream(data );
}
}
class GZipCompression implements Compression{
@Override
public OutputStream compress(OutputStream data) throws IOException {
return new GZIPOutputStream(data );
}
}
public static void main(String[] args) throws IOException {
//传统方式
new Compressor(new GZipCompression()).compress(); ;
new Compressor(new ZipCompression()).compress(); ;
//lambda方式
//我们可以看到用lambda 方式完全不用实现 ZipCompression GZipCompression类了
new Compressor(GZIPOutputStream::new).compress();
new Compressor(ZipOutputStream::new).compress();
}
Stream
stream API 可能是JDK8中最大的改变 改变我们操作流的习惯
使用集合时调用 .stream() 转为流操作模式 JDK8的集合 都带有这个方法 (Map没有) 下面我们使用stream API重构我们的集合操作
随机找一段 项目中的代码进行重构
-
使用stream
// 这段代码是一个model list 转为 ztree list 的方法
public List<ZTree> getZTree() {
List<ZTree> list = Lists.newArrayList();
for (Res r : super.findAll()) {
ZTree zt = new ZTree(r.getId(), r.getName(), r.getPid());
if (zt.id == 0) zt.setDisCheck(true);
list.add(zt);
}
return list;
}
public List<ZTree> getZTree() {
return super.findAll()
.stream()
.map(this::getZtree())
.collect(Collectors.toList());
}
public Ztree getZtree(Model r){
ZTree zt = new ZTree(r.getId(), r.getName(), r.getPid());
if (zt.id == 0) zt.setDisCheck(true);
return zt;
}
代码解析
.map() 接受一个lambda 表达式 对集合类型进行转换 这里是对数据库查询到的对象 转化为 ZTree对象
.collect() 最后转化为新的集合
-
重构和定制收集器
下面我们来看一个更复杂的重构我们的代码
同时使用到了自定义的收集器
我们需要把 一个Song的集合 的名字进行格式化处理
class Song {
private String name;
public long length;
public String getName() {
return name;
}
}
/**
* 格式化 v1.0
* 我们原有的代码是这样的
* 看起来好像没啥问题
* 还是好像不太方便重用
* @param list
* @return
*/
public String getName(List<Song> list) {
StringBuffer sb = new StringBuffer("[");
for (Song song : list) {
if (sb.length() > 1) sb.append(",");
sb.append(song.getName());
}
return sb.toString();
}
/**
* 格式化 v2.0
* <p>
* 只是用了jdk8的特性
* 但是
* 好像并没有太大改进
*
* @param list
* @return
*/
public String getName2(List<Song> list) {
StringBuffer sb = new StringBuffer("[");
list.stream()
.map(Song::getName)
.forEach(name -> {
if (sb.length() > 1) sb.append(",");
sb.append(name);
});
return sb.toString();
}
/**
* 格式化 v3.0
* <p>
*
* foreach的操作似乎太过笨重
* 我们使用reduce 完成这个过程
*
* 但是似乎让代码更糟糕啦
*
* @param list
* @return
*/
public String getName3(List<Song> list) {
return list.stream()
.map(Song::getName)
.reduce(new StringBuilder(), (builder, name) -> {
if (builder.length() > 0) builder.append(",");
builder.append(name);
return builder;
}, (left, right) -> left.append(right))
.insert(0, "[").append("]").toString();
}
/**
* 格式化 v4.0
*
*我们用StringCombiner
* 来代替原先的操作
*来隐藏杂乱无章的细节
*
* 看起来是不是好了点
*
* @param list
* @return
*/
public String getName4(List<Song> list) {
return list.stream().map(Song::getName)
.reduce(new StringCombiner(",", "[", "]"),
StringCombiner::add,
StringCombiner::merge).toString();
}
class StringCombiner {
private final String delim;
private final String prefix;
private final String suffix;
private final StringBuilder builder;
public StringCombiner(String delim, String prefix, String suffix) {
this.delim = delim;
this.prefix = prefix;
this.suffix = suffix;
builder = new StringBuilder();
}
public StringCombiner add(String element) {
if (areAtStart()) {
builder.append(prefix);
} else {
builder.append(delim);
}
builder.append(element);
return this;
}
private boolean areAtStart() {
return builder.length() == 0;
}
public StringCombiner merge(StringCombiner other) {
if (other.builder.length() > 0) {
if (areAtStart()) {
builder.append(prefix);
} else {
builder.append(delim);
}
builder.append(other.builder, prefix.length(), other.builder.length());
}
return this;
}
@Override
public String toString() {
if (areAtStart()) {
builder.append(prefix);
}
builder.append(suffix);
return builder.toString();
}
}
/**
* 格式化 v5.0
*
* 之前的代码看起来不错了
* 但是还不能在程序中重用
* 我们自定义一个收集器
* 这样看起来是不是更好了
*
* @param list
* @return
*/
public String getName5(List<Song> list) {
return list.stream().map(Song::getName).collect(new StringCollector(".","[","]"));
}
class StringCollector implements Collector<String, StringCombiner, String> {
private final Set<Characteristics> characteristics = Collections.emptySet();
private final String delim;
private final String prefix;
private final String suffix;
public StringCollector(String delim, String prefix, String suffix) {
this.delim = delim;
this.prefix = prefix;
this.suffix = suffix;
}
public Supplier<StringCombiner> supplier() {
return () -> new StringCombiner(delim, prefix, suffix);
}
public BiConsumer<StringCombiner, String> accumulator() {
return StringCombiner::add;
}
public BinaryOperator<StringCombiner> combiner() {
return StringCombiner::merge;
}
public Function<StringCombiner, String> finisher() {
return StringCombiner::toString;
}
public Set<Characteristics> characteristics() {
return characteristics;
}
并行计算
在多核cpu的时代并行计算能很大的提升程序的速度
在stream api中使用.parallelStream();替换stream()就可以直接拥有并行计算的能力
当然也不能随便使用基本上要注意几点
1)处理的数据量 如果太小处理管道花费的时间远大于单线程的时间
2)单核cpu 就没有必要使用了
3)单元处理开销在每个元素身上处理的时间越长并行越有意义
好了大家都学会了么 赶紧用起来吧
参考资料 Java 8 Lambdas Functional Programming for the Masses
来源:oschina
链接:https://my.oschina.net/u/1022411/blog/660840