使用JDK8新特性重构你的代码

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      

                                                                                                                                     


你可能感兴趣的:(使用JDK8新特性重构你的代码)