首页 文章

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

提问于
浏览
12

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

  • 如果其中任何一个非空,则结果应为非空名称 .

  • 如果两者都非空,我希望结果与分隔符 AND 连接 .

  • 如果两者都为空,结果应为空 Optional

我的尝试:

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 回答

  • 1

    一种解决方案是流和 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组合 .

  • 0

    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);
    
  • 14

    使用 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类而不是在执行 + 操作时创建一个新类 .

  • 7

    有时,尝试使用 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 时做出的选择 .

  • 2

    接近接受的答案

    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);
    
  • 0

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

    它接受 ListOptional ,所以它涵盖了一个更普遍的情况而不仅仅是两个元素 .

    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);
    }
    
  • 3

    没有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()));
        }
    

相关问题