在Java 8中连接两个或多个可选字符串

时间:2017-09-28 15:33:18

标签: string java-8 concatenation optional

对你们我有一个相当简单的问题。在Java 8中,引入了Optional类型。我有两个Optional<String>类型的对象,我想知道连接它们的更优雅的方法。

Optional<String> first = Optional.ofNullable(/* Some string */);
Optional<String> second = Optional.ofNullable(/* Some other string */);
Optional<String> result = /* Some fancy function that concats first and second */;

详细地说,如果两个原始Optional<String>对象中的一个等于Optional.empty(),我希望整个连接也是空的。

请注意,我不是要问如何连接Java中两个Optional的评估,而是如何连接一些String内的Optional

提前致谢。

6 个答案:

答案 0 :(得分:5)

我找到的解决方案如下:

first.flatMap(s -> second.map(s1 -> s + s1));

可以使用专用方法清理,如下所示:

first.flatMap(this::concat);
Optional<String> concat(String s) {
    second.map(s1 -> s + s1);
}

但是,我认为可以找到更好的东西。

如果我们想要推广到Optional<String>的列表或数组,那么我们可以使用类似于以下的内容。

Optional<String> result =
    Stream.of(Optional.of("value1"), Optional.<String>empty())
          .reduce(Optional.of(""), this::concat);

// Where the following method id used
Optional<String> concat(Optional<String> first, Optional<String> second) {
    return first.flatMap(s -> second.map(s1 -> s + s1));
}

请注意,为了编译上面的代码,我们必须手动将Optional.empty()的类型变量绑定到String

答案 1 :(得分:1)

@SafeVarargs
public final Optional<String> concat(Optional<String>... inputs)
{
    return Arrays.stream(inputs)
        .reduce((left, right) -> left.flatMap(leftValue -> right.map(rightValue -> leftValue + rightValue)))
        .get();
}

@Test
public void shouldReturnEmptyIfFirstItemIsEmpty()
{
    assertThat(concat(Optional.empty(), Optional.of("B")), is(Optional.empty()));
}

@Test
public void shouldReturnEmptyIfSecondItemIsEmpty()
{
    assertThat(concat(Optional.of("A"), Optional.empty()), is(Optional.empty()));
}

@Test
public void shouldConcatIfNoItemIsEmpty()
{
    assertThat(concat(Optional.of("A"), Optional.of("B")), is(Optional.of("AB")));
}

这是在Stream上使用reduce方法的实现。

答案 2 :(得分:0)

您可以使用以下内容:

Optional<String> result;
result = first.isPresent() && second.isPresent() ?  Optional.of(first.get() + second.get()) : Optional.empty();

答案 3 :(得分:0)

任何需要灵活数量的可选字符串的解决方案都必须明确使用StringBuilder,而不是依靠编译器为您生成一个。

String concatThem(Stream<String> stringsin) {
    StringBuilder sb = new StringBuilder();
    stringsin.forEach(s -> sb.append(s));
    return sb.toString();
}

如果您有Stream<Optional<String>>,则会变为:

String concatThem(Stream<Optional<String>> stringsin) {
    StringBuilder sb = new StringBuilder();
    stringsin.filter(Optional::isPresent).forEach(s -> sb.append(s.get()));
    return sb.toString();
}

否则,如果您有N个可选字符串,则最终会产生和破坏N-1个一次性StringBuilder对象(在编译时生成)和N-1个字符串的大量循环。

编辑:我误读了,所以如果有任何一个缺失的话,请按照以下方式进行操作:

String concatThem(Stream<Optional<String>> stringsin) {
    StringBuilder sb = new StringBuilder();
    try {
        stringsin.forEach(s -> {
            if (!s.isPresent()) throw new IllegalArgumentException();
            sb.append(s.get())
        });
    }
    catch(IllegalArgumentException ex) {
        sb.setLength(0);
    }
    return sb.toString();
}

当然,如果你坚持使用新的API来理解语法并且执行起来很重。

答案 4 :(得分:0)

这是另一种非常好的方式:

@Value.Immutable
public abstract class Person {

    public Optional<String> firstName() {
        return Optional.of("John");
    }

    public Optional<String> lastName() {
        return Optional.of("Smith");
    }

    public Optional<String> location() {
        return Optional.of("Paris");
    }

    @Value.Lazy
    public String concat() {

        return Stream.of(firstName(), lastName(), location())
                .filter(Optional::isPresent)
                .map(Optional::get)
                .filter(StringUtils::isNotBlank)
                .reduce((first, second) -> first + '.' + second)
                .orElse("");
    }
}

请注意,正如其他注释中所提到的,concat()方法在不使用StringBuilder的情况下执行字符串连接(如果您多次调用该方法,则可能无法执行此操作)。为了解决这个问题,在上面的例子中我们使用了Immutables'[1] @ Value.Lazy ,这确保了一次调用concat()方法并缓存结果以便进一步调用。效果很棒!

[1] https://immutables.github.io

答案 5 :(得分:0)

您可以流Optional并用concat缩小它们。

Optional<String> first = Optional.of("foo");
Optional<String> second = Optional.of("bar");
Optional<String> result = Stream.of(first, second).flatMap(Optional::stream).reduce(String::concat);

如果您使用的是Java 8,则将flatMap替换为filter(Optional::isPresent).map(Optional::get)

还考虑使用joining收集器:这将返回String,而不是Optional<String>