2017-09-28 27 views
4

我有一个相当简单的问题给你们。在Java 8中,它引入了Optional类型。我有Optional<String>类型的两个对象,我想知道哪个是更加优雅的方式来连接它们。在Java 8中连接两个或两个以上的可选字符串

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(),我想整个级联是空的了。

请注意,我不问如何来连接两个Optional S中的评价在Java中,但如何连接两个String S中的一些Optional内。

在此先感谢。

回答

3

我找到的解决方案是:

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

除非您有半群二进制运算符,flatmap是组合两个选项的最佳方式。就像在斯卡拉你可以做'first | + |第二个' – soote

+0

不是,这是你在Java 8中可以做的最好的。 –

+0

所以,你说我不能做得更好:(你能解释一下“半群”吗? –

0

您可以使用类似:

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

也占了其中只有一个是不存在的情况下 – ds011591

+0

@ ds011591是没有,条件是''&&所以 – nullpointer

+0

如果一个人,而不是其他然后返回什么?身份证认为你想要返回当时在场的价值。 – ds011591

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,在重量轻语法和重执行。

+0

你知道你的解决方案不尊重重要的基本约束吗?如果任何'String'是空的,结果'String'也应该是空的。无论如何,我认为有一种更实用的方法可以通过。 –

+0

看看我更新的答案。我使用了更多功能和习惯用法。 –

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"))); 
} 

下面是一个使用上流减少方法的实现。

+1

你知道每次使用'@ SafeVarargs'时,一只小猫会死在某个地方吗? –

+0

我用一个更安全的解决方案更新了我的答案。一探究竟。 –

+0

我想''SafeVarargs'也被用在诸如'Arrays.asList(T ... a)'之类的东西里面,有很多死小猫。如果它涉及到你,你总是可以接受一个Collection或Stream作为该方法的参数... –

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