如何在Java 8中使用分隔符连接两个可选<string>

时间:2018-02-16 22:53:49

标签: java java-8 optional

我有两个Optional字符串,name1name2。我想加入这两个,结果也是一个可选的:

  1. 如果其中任何一个非空,则结果应为非空名称。
  2. 如果两者都非空,我希望结果与分隔符AND结合。
  3. 如果两者都为空,则结果应为空Optional
  4. 我的尝试:

    StringBuilder sb = new StringBuilder();
    name1.ifPresent(sb::append);
    name2.ifPresent(s -> {
        if (sb.length() > 0) {
            sb.append(" AND ");
        }
        sb.append(s);
    }
    Optional<String> joinedOpt = Optional.ofNullable(Strings.emptyToNull(sb.toString()));
    

    这很有效,但看起来很丑陋且不太实用。

    PS:有类似的question,但接受的答案是错误的。具体来说,如果name1为空并且name2不为空,则返回空的可选项。

7 个答案:

答案 0 :(得分:14)

一种解决方案是流和reduce()

Optional<String> joinedOpt = Stream.of(name1, name2)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .reduce((a, b) -> a + " AND " + b);

如其他人所建议的那样,随意用Java 9或Guava替换filter / map组合。

答案 1 :(得分:7)

Java 1.9中可能的解决方案是

 Optional<String> name1 = Optional.of("x");
 Optional<String> name2 = Optional.of("y");
 String s = Stream.concat(name1.stream(), name2.stream()).collect(Collectors.joining(" AND "));
 System.out.println(s);

使用Java 1.8,您无法从Optional到Stream。但是,您可以非常轻松地将此类转换添加到Java 1.8。

static <T> Stream<T> of(Optional<T> option) {
    return option.map(Stream::of).orElseGet(Stream::empty);
}

现在您可以使用of方法连接两个流。

现在,如果没有结果,要获得一个空的Optional,你可以将结果包装成一个Optional并做一个简单的过滤器。

Optional<String> result = Optional.of(collect).filter(Optional::isPresent);

答案 2 :(得分:3)

使用Collectors.joining

略有不同的味道
    Optional<String> result  = Optional.of( 
        Stream.of(so1, so2)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect( Collectors.joining(" AND ") ) 
    ).filter( s -> !s.isEmpty() ); 

我喜欢Collectors.joining的是,Java流可能会重复使用单个StringBuilder类,而不是在执行+操作时创建一个新类。

答案 3 :(得分:2)

有时,尝试使用Optional(或Stream)功能只会模糊您在代码中尝试表达的内容。

对我来说,这里的主要想法是,如果两个值都存在,它们应该与“AND”连接。以下是对此的关注:

Optional<String> joinedOpt;
if (name1.isPresent() && name2.isPresent()) {
    joinedOpt = Optional.of(name1.get() + " AND " + name2.get());
} else {
    joinedOpt = name1.isPresent() ? name1 : name2;
}

我可能会误解他的信息,但这种方法受到Stuart Marks' answer to a similiar question,的启发,他在his Devoxx talk中更深入地讨论了他帮助设计Optional时做出的选择。

答案 4 :(得分:1)

接近接受的答案

Optional<String> joinedOpt = Stream.of(name1, name2)
            .flatMap(x -> x.map(Stream::of).orElse(null))
            .reduce((a, b) -> a + " AND " + b);

或者在java-9中:

Optional<String> joinedOpt = Stream.of(name1, name2)
            .flatMap(Optional::stream)
            .reduce((a, b) -> a + " AND " + b);

答案 5 :(得分:0)

这是一个流式解决方案,它会过滤那些存在的Optional,然后提取元素并使用内置的收集器来连接其余的元素。然后它测试结果是否为空,返回空Optional

它接受List Optional个,因此它涵盖了一般情况,而不仅仅是两个元素。

public Optional<String> optionalJoin(List<Optional<String>> strings) {
    String result = strings.stream()
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.joining(" AND "));
    return result.isEmpty() ? Optional.empty() : Optional.of(result);
}

答案 6 :(得分:0)

没有Streams的单行,但不是特别优雅:

    public static Optional<String> concat(Optional<String> name1, Optional<String> name2) {
        return Optional.ofNullable(name1.map(s1 -> name2.map(s2 -> s1 + " AND " + s2).orElse(s1)).orElse(name2.orElse(null)));
    }

    public static void main(String[] args) {
        System.out.println(concat(Optional.of("A"), Optional.of("B")));
        System.out.println(concat(Optional.of("A"), Optional.empty()));
        System.out.println(concat(Optional.empty(), Optional.of("B")));
        System.out.println(concat(Optional.empty(), Optional.empty()));
    }