通过流加入字符串

时间:2017-02-07 09:48:49

标签: java-8

我想加入一个名单:

List<String> names;
names = books.stream()
        .map( b -> b.getName() )
        .filter( n -> ( (n != null) && (!n.isEmpty()) ) )
        .collect(Collectors.joining(", "));

这不会编译说:

  

不兼容的类型。推理变量R具有不兼容的边界

经过一些研究,似乎有些事我误解了。我认为.map( b -> b.getName() )返回/将类型更改为String,看起来似乎有些问题。如果我使用.map(Book::getName)代替,我仍然会收到错误,但我可能会don't fully understand the difference

然而,这并没有抱怨:

List<String> names;
names = books.stream()
        .map( b -> b.getName() )
        .map( Book::getName )
        .filter( n -> ( (n != null) && (!n.isEmpty()) ) )
        .collect(Collectors.joining(", "));

有人可以解释我为什么吗?关于.map( b -> b.getName() ).map(Book::getName)之间差异的一些教学解释也很受欢迎,因为我认为我没有做对。

3 个答案:

答案 0 :(得分:7)

joining(", ")收藏家将使用传递的字符串收集加入所有字符串到单个字符串。在这种情况下,collect的撤销类型为String,但您尝试将结果分配给List

如果要收集字符串列表,请使用Collectors.toList()

如果您有Book个实例的集合,那么您就足以将Book的流映射到String的流一次。

lamdba&amp; amp;方法参考

  • lamdba通常有一个运营商块:

    b -> {
        // here can be other operators
        return b.getName(); // single operator
    }
    

    如果lambda有单个运算符,则可以缩短它:

    b -> b.getName()
    
  • 方法参考只是&#34;快捷方式&#34;对于具有单个运算符的lambda:

    b -> b.getName()
    

    可以替换为

    Book::getName
    

    但如果你有这样的lambda:

    b -> b.getName().toLowerCase()
    

    这里你不能引用getName方法,因为你需要做额外的电话。

答案 1 :(得分:3)

如果您使用的是Collectors.joining(),则结果将是单个串联字符串:

String names = books.stream()
        .map( b -> b.getName() )
        .filter(n -> (n != null) && !n.isEmpty())
        .collect(Collectors.joining(", "));

Collectors.toList()是返回List的那个:

List<String> names = books.stream()
        .map( b -> b.getName() )
        .filter(n -> (n != null) && !n.isEmpty())
        .collect(Collectors.toList());

Book::getNamemethod reference,其结果与b -> b.getName()相同。方法引用更清晰,只要传递的方法符合预期functional interface的签名,就可以将其他现有方法作为参数传递给map()等方法。在这种情况下,map()需要Function接口的实例。因此,您可以从这样的接口提供符合抽象R apply(T t)方法签名的方法的任何引用。

由于您要将Book映射到String,因此要为map()方法提供的方法的实际签名必须为String apply(Book t)。这可以理解为“接收书籍并返回字符串”。这样,您传递的任何符合此定义的方法都是有效的。当您传递方法引用Book::getName时,getName方法本身不符合上面提供的签名(因为它根本没有参数),但它符合此类签名的定义: 您传递一本书并从其名称返回一个字符串。

因此,请考虑一下,在您拥有图书清单的班级中,您还有一个方法可以对Book执行任何操作,并返回String。下面的方法是一个示例,它接收Book并从其名称中获取前10个字符:

public String getReducedBookName(Book b){
  if(b.getName() == null)
     return "";

  String name = b.getName();
  return name.substring(0, name.length() > 10 ? 10 : name.length());
}

您也可以将此方法(不在Book类中)作为参数传递给map()方法:

String names = books.stream()
            .map(this::getReducedBookName)
            .filter(n -> !n.isEmpty())
            .collect(Collectors.joining(", "));

答案 2 :(得分:0)

如果你喜欢 mapping over map

作为String

String names = books.stream().collect(mapping(Book::getName,
    filtering(s -> s != null && ! s.isBlank(),
      joining(", "))));

作为List

List<String> names = books.stream().collect(mapping(Book::getName,
    filtering(s -> s != null && ! s.isBlank(),
      toList())));