【问题标题】:Joining strings with limit加入有限制的字符串
【发布时间】:2016-03-04 18:02:30
【问题描述】:

仅使用标准 Java 库,有什么简单的机制可以将字符串连接到一个限制,并在限制导致更短的字符串时附加一个省略号?

效率是可取的。连接所有字符串然后使用String.substring() 可能会消耗过多的内存和时间。可以在 Java 8 流管道中使用的机制更可取,这样甚至可能永远不会创建超过限制的字符串。

出于我的目的,我对以下任一表示的限制感到满意:

  • 要加入的最大字符串数
  • 结果中的最大字符数,包括任何分隔符。

例如,这是在 Java 8 中使用标准库强制连接字符串的最大数量的一种方法。有没有更简单的方法?

final int LIMIT = 8;

Set<String> mySet = ...;
String s = mySet.stream().limit( LIMIT ).collect( Collectors.joining(", "));
if ( LIMIT < mySet.size()) {
    s += ", ...";
}

【问题讨论】:

  • 这个问题不是要求“推荐或查找书籍、工具、软件库、教程或其他非现场资源。”

标签: java java-8 java-stream


【解决方案1】:

您可以为此编写自定义收集器。这个是基于another I wrote for a similar case

private static Collector<String, List<String>, String> limitingJoin(String delimiter, int limit, String ellipsis) {
    return Collector.of(
                ArrayList::new, 
                (l, e) -> {
                    if (l.size() < limit) l.add(e);
                    else if (l.size() == limit) l.add(ellipsis);
                },
                (l1, l2) -> {
                    l1.addAll(l2.subList(0, Math.min(l2.size(), Math.max(0, limit - l1.size()))));
                    if (l1.size() == limit) l1.add(ellipsis);
                    return l1;
                },
                l -> String.join(delimiter, l)
           );
}

在这段代码中,我们保留了所有 encoutered 字符串的 ArrayList&lt;String&gt;。当一个元素被接受时,当前列表的大小将根据限制进行测试:严格小于它,添加元素;等于它,添加省略号。组合器部分也是如此,这有点棘手,因为我们需要正确处理子列表的大小以不超过限制。最后,finisher 只需使用给定的分隔符加入该列表。

此实现适用于并行流。它将保留 Stream in encounter order 的头部元素。请注意,即使在达到限制后没有添加任何元素,它也会消耗 Stream 中的所有元素。

工作示例:

List<String> list = Arrays.asList("foo", "bar", "baz");
System.out.println(list.stream().collect(limitingJoin(", ", 2, "..."))); // prints "foo, bar, ..."

【讨论】:

  • 谢谢。如有必要,可以通过在 collect() 之前插入 limit(LIMIT + 1) 来避免消耗 Stream 中的所有元素。
  • @AndyThomas 是的。但收集器本身并没有短路。
  • +1 用于很好地实现字符串数连接器。多个字符的连接器会更复杂。
【解决方案2】:

虽然使用第三方代码不是提问者的选择,但其他读者可能会接受。即使编写自定义收集器你仍然有一个问题:整个输入将被处理为标准收集器不能短路(特别是不可能处理无限流)。我的StreamEx 库增强了收集器的概念,使创建短路收集器成为可能。 Joining 收集器也很容易提供:

StreamEx.of(mySet).collect( 
    Joining.with(", ").ellipsis("...").maxChars(100).cutAfterDelimiter() );

保证结果不超过 100 个字符。可以使用不同的计数策略:您可以按字符、代码点或字素进行限制(不计算组合的 Unicode 字符)。您也可以在任何位置(“第一个条目,第二个 en...”)或单词之后(“第一个条目,第二个 ...”)或分隔符之后(“第一个条目,...”)剪切结果,或在分隔符之前(“第一个条目,第二个条目......”)。它也适用于并行流,但在有序情况下可能效率不高。

【讨论】:

    【解决方案3】:

    仅使用标准 Java 库

    我不相信那里有任何东西可以满足你的要求。

    您需要编写自己的Collector。它不会那么复杂,所以我不明白为什么编写自己的会是一个问题。

    【讨论】:

    • 我想到了。我可以limit(LIMIT + 1) 只遍历流的一部分,但仍然允许自定义收集器知道已达到限制。希望有一些我错过的更简单的东西。
    • 限制字符串的 number 对我来说似乎毫无意义。它应该限制结果的长度,这意味着limit() 不是答案。
    • 在某些情况下确实如此。但是,我有一个先验已知字符串很短的情况。不需要精确的字符限制;我只想包含整个字符串。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多