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