如何使用Optional和Streams完成操作?

时间:2018-08-01 01:26:19

标签: java java-stream optional

public class Product {

    private String name;
    private List<Image> images;
    //getters and setters

      public class Images {
        private String url;
        private List<Thumbnail> thumbnails;

        // getters and setters

        public class Thumbnail{

          //getters and setters
          private String url;
        }
      }
    }
}

我上这堂课。我需要获取产品名称,第一张图片的网址和第一张缩略图的网址。我还需要确保产品,图像和缩略图不是空的。

这就是我要尝试的方式:

Optional.ofNullable(product).ifPresent(productData -> {
  response.setProductName(productData.getName());

  Optional.ofNullable(productData.getImages()).ifPresent(images -> {
    images.stream().findFirst().ifPresent(image -> {
      response.setImageUrl(image.getUrl());

        Optional.ofNullable(image.getThumbnails()).ifPresent(thumbnails -> {
        thumbnails.stream().findFirst().ifPresent(thumbnail -> {
          response.getThumbnailUrl();
        });
      });
    });
  });
});

有更好的方法吗?

4 个答案:

答案 0 :(得分:3)

您永远不会拥有null列表。空列表表示同一件事,而无需引入空检查。这样一来,您就不必进行两次ifPresent检查。

Optional.ofNullable(product).ifPresent(productData -> {
  response.setProductName(productData.getName());

  productData.getImages().stream().findFirst().ifPresent(image -> {
    response.setImageUrl(image.getUrl());

    image.getThumbnails().stream().findFirst().ifPresent(thumbnail -> {
      response.getThumbnailUrl();
    });
  });
});

可选项和流实际上并没有帮您任何忙。不要仅仅因为它们存在而使用它们。考虑只使用常规的if语句。

if (product == null) {
    return;
}

if (!product.getImages().isEmpty()) {
    Image image = product.getImages().get(0);
    response.setImageUrl(image.getUrl());

    if (!image.getThumbnails().isEmpty()) {
        response.getThumbnailUrl();
    }
}

或者,您可以使用stream().findFirst().ifPresent()循环来模拟for,这些循环在第一次迭代后就会中断。

if (product == null) {
    return;
}

for (Image image: product.getImages()) {
    response.setImageUrl(image.getUrl());

    for (Thumbnail thumbnail: image.getThumbnails()) {
        response.getThumbnailUrl();
        break;
    }

    break;
}

答案 1 :(得分:3)

@John的答案是正确的。如果可以,请返回空列表。

也可能是您想要在没有任何项目和没有结果之间进行区分(在您的情况下这没有多大意义,但我们在假设地说)。然后返回Optional<List<T>>而不是返回null然后进行转换。那么@Johannes的答案就是正确的答案。

考虑该问题的另一种方法是,如果您无法控制返回值,则将其转换为流以链接调用:

Optional.ofNullable(possiblyNullProduct).stream()
  .peek(product -> response.setProductName(product.getName()))
  .map(Product::getImages)
  .filter(Objects::nonNull)
  .map(images -> images.stream().findFirst())
  .filter(Optional::isPresent).map(Optional::get)
  .peek(image -> response.setImageUrl(image.getUrl())
  .map(Image::getThumbnails)
  .filter(Objects::nonNull)
  .map(thumbnails -> thumbnails.stream().findFirst())
  .filter(Optional::isPresent).map(Optional::get)
  .forEach(thumbnail -> response.getThumbnailUrl());

Optional::stream was added in Java 9

这只是另一个解决方案,绝不是更好的解决方案。我欢迎对此演出发表任何评论。

获取每个列表的第一项的另一个选项是转换为Optional并返回到Stream中:

  .map(Product::getImages)
  .filter(Objects::nonNull)
  .flatMap(List::stream).findFirst().stream() // <- changed here
  .peek(image -> ...)

您还可以通过类似的方式更改最后三行:

  .map(Image::getThumbnails)
  .filter(Objects::nonNull)
  .flatMap(List::stream).findFirst() // <- from here
  .ifPresent(thumbnail -> response.getThumbnailUrl());

答案 2 :(得分:2)

是的,flatMap在这里会有所帮助:

Optional<Product> optProduct = Optional.ofNullable(product);
optProduct.ifPresent(p -> response.setProductName(p.getName()));
Optional<Image> optImage = optProduct.flatMap(p -> Optional.ofNullable(p.getImages()))
    .stream().flatMap(il -> il.stream()).findFirst();
optImage.ifPresent(i -> response.setImageUrl(i.getUrl()));
optImage.flatMap(i -> Optional.ofNullable(i.getThumbnails()))
    .stream().flatMap(tl -> tl.stream()).findFirst()
    .ifPresent(t -> response.getThumbnailUrl()); // WTF?

在Java 9中添加了Optional.stream()的节点。

答案 3 :(得分:1)

我认为可选方案在这里没有太大帮助。我将通过抽象迭代逻辑来清理代码,如下所示:

private <T> void findFirst(List<T> list, Consumer<T> action) {
    if (list != null) {
        list.stream()
            .findFirst()
            .ifPresent(action);
    }
}

if (product != null) {
    response.setProductName(productData.getName());
    findFirst(productData.getImages(), image -> {
        response.setImageUrl(image.getUrl());
        findFirst(image.getThumbnails(), thumbnail -> response.setThumbnailUrl(thumbnail.getUrl()));
    });
}