Functional programming 如何使用delimeter连接字符串,delimeter以java8函数式在内部级别连接字符串

Functional programming 如何使用delimeter连接字符串,delimeter以java8函数式在内部级别连接字符串,functional-programming,java-8,java-stream,Functional Programming,Java 8,Java Stream,我有Map我的地图带结构: { "key1" : ["val1","val2","val3"] "key2" : ["val4","val5","val6"] "key3" : ["val7","val8"] } 我可以使用收集器将每个值与键联接。联接: val1-key1-val2-key1-val3 val4键2 val5键2 val6 val7键3 val8 但在全球范围内,我还需要将所有值合并到一个字符串中: "val1 key1 val2 key1 va

我有
Map我的地图带结构:

{
     "key1" : ["val1","val2","val3"]
     "key2" : ["val4","val5","val6"]
     "key3" : ["val7","val8"]
}
我可以使用
收集器将每个值与键联接。联接

val1-key1-val2-key1-val3

val4键2 val5键2 val6

val7键3 val8

但在全球范围内,我还需要将所有值合并到一个字符串中:

"val1 key1 val2 key1 val3 key2 val4 key2 val5 key2 val6 key3 val7 key3 val8"
                           ^                             ^        
其中值由右值中的键连接


是的,我可以以命令式的方式实现这一点,但我问我如何使用流api实现这一点。

首先,应该强调的是,只有在定义了遭遇顺序的情况下,此请求才有意义,这不适用于普通的
HashMap
HashSet

对于具有稳定顺序的映射和集合,例如
LinkedHashMap
TreeMap
及其
Set
对应项,如果我们可以使用以下内容,那就太好了:

String s=myMap.entrySet().stream()
    .map(e -> e.getValue().stream().collect(
         ()->new StringJoiner(" "+e.getKey()+" "),StringJoiner::add, StringJoiner::merge))
    .collect(()->new StringJoiner(""), StringJoiner::merge, StringJoiner::merge)
    .toString();
但它不起作用,因为
StringJoiner::merge
将使用左
StringJoiner
的分隔符,而不是右分隔符

由于此行为是不可配置的,因此我们需要自己的
StringJoiner变体

final class RightHandJoiner {
    String separator;
    StringBuilder content;

    public RightHandJoiner(String sep) {
        separator = sep;
    }
    public RightHandJoiner add(CharSequence next) {
        if(content==null) content=new StringBuilder(next);
        else content.append(separator).append(next);
        return this;
    }
    public RightHandJoiner merge(RightHandJoiner next) {
        if(next.content!=null) {
            if(content==null) {
                content=new StringBuilder(next.content);
                separator=next.separator;
            }
            else content.append(next.separator).append(next.content);
        }
        return this;
    }
    public String toString() {
        return content==null? "": content.toString();
    }
}
这是有意简化的,只为您的任务服务,例如,它不支持非空前缀和后缀字符串。使用此类替换
StringJoiner
,您将得到一个有效的解决方案:

String s=myMap.entrySet().stream()
    .map(e -> e.getValue().stream()
        .collect(() -> new RightHandJoiner(" "+e.getKey()+" "),
                 RightHandJoiner::add, RightHandJoiner::merge))
    .collect(()->new RightHandJoiner(""), RightHandJoiner::merge, RightHandJoiner::merge)
    .toString();

另一种方法是两次
flatMap
,并构建键值字符串管道:

String s = myMap.entrySet().stream().flatMap(
                 e -> e.getValue().stream().flatMap(val -> Stream.of(e.getKey(), val))
           ).collect(Collectors.joining(" "));
System.out.println(s);
将为您提供:

键1 val1键1 val2键1 val3键2 val4键2 val5键2 val6键3 val7键3 val8

必须跳过第一个键字符串才能获得预期的输出

String s = myMap.entrySet().stream().flatMap(
                 e -> e.getValue().stream().flatMap(val -> Stream.of(e.getKey(), val))
           ).skip(1).collect(Collectors.joining(" "));
System.out.println(s);
val1键1 val2键1 val3键2 val4键2 val5键2 val6键3 val7键3 val8


您是否正在使用
NavigableMap
SortedSet
?发布命令式代码。这是一个优雅的解决方案,但不符合要求。看看问题中的预期结果。非常好,非常优雅,谢谢。我没想到流api会如此强大:)