对你们我有一个相当简单的问题。在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
。
提前致谢。
答案 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()方法并缓存结果以便进一步调用。效果很棒!
答案 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>
。