Java Lambda从列表中获取元素

时间:2015-11-06 01:40:11

标签: java lambda java-8 java-stream

我有以下代码,它使用了很好的旧java:

List<Bar> repo = ArrayList<>();
public Bar foo(int id) {
    for(Bar c: repo){
        if(c.getId() == id)
            return c;
    }
    Bar target = new Bar();
    target.setFooo("");
    target.setId(0);
    return target;
}

然而,我试图让它变得更好一点,(即只是想学习lambdas

public Bar foo(int id) {
    Bar target = repo.stream().filter(c -> c.getId() == id)
               .findFirst().orElse(null);
    if(target == null){  
        target = new Bar();
        target.setFooo("");
        target.setId(0);
    }
    return target;
}

但上面的代码返回ArrayOutOfBounds异常,我不确定如何(因为它是一个列表)或为什么。

2 个答案:

答案 0 :(得分:6)

如果要使用函数式编程技术,最好重构代码,以便能够使用构造函数,构建器或工厂方法完全构造对象。塞特斯是坏朋友,因为他们暗示可变性和功能编程喜欢不可改变的东西。

所以添加新的Bar构造函数:

public Bar(String fooo, int id) {
    this.fooo = fooo;
    this.id = id;
}

在此之后您可能会意识到所有Bar对象都可以在没有setter的情况下使用。如果是这样,您只需删除setter并将Bar字段设为final,这样Bar将变为不可变。即使你无法摆脱其他地方的setter,拥有新的构造函数,你的foo方法也可以用更清晰的方式重写:

public Bar foo(int id) {
    return repo.stream()
               .filter(c -> c.getId() == id)
               .findFirst()
               .orElseGet(() -> new Bar("", 0));
}

答案 1 :(得分:1)

你的解决方案非常接近我的建议:

public Bar foo(int id) {
    return repo.stream()

      // find by id
      .filter(c -> c.getId() == id)

      // choose any
      .findAny()

      .orElseGet(() -> {
          target = new Bar(); 
          target.setFooo("");
          target.setId(0);
          return target;
      });
}

不要创建更复杂的构造函数。它们甚至经常被称为反模式。坚持你的二传手!

如果要优化,请让您的setter返回更新的对象。这种方法将来允许添加任意数量的setter。那看起来像那样:

public Bar setFooo(String fooo) {
    this.fooo = fooo;
    return this; // return modified instance
}

// do the same for setId()

稍后在代码中:

.orElseGet(() -> new Bar()
      .setFooo("")
      .setId(0)
      // add any number of future setters here
);