繁体   English   中英

java8流和状态

[英]java8 streams and state

我写了这段代码,很累在这里使用流,但是实现很麻烦,使用传统的for-each循环会更好。 有什么更好的方法来使用流吗?

它非常简单的XML生成器来自两部分。

    input = "aaa.zzz\r\n"
            + "  \r\n"
            + "aaa.xxxx    \r\n"
            + "bbb.cccc";

输出:

<aaa><xxxx>xxxx</xxxx>
<zzz>zzz</zzz>
</aaa><bbb><cccc>cccc</cccc>
</bbb>

码:

public static void main(String[] args) {
  String input = "aaa.zzz\r\n"
          + "  \r\n"
          + "aaa.xxxx    \r\n"
          + "bbb.cccc";
  List<String> list = Arrays.stream(input.split("\r\n")).map(String::trim)
          .filter(q -> !q.isEmpty())
          .sorted()
          .map(PathToXml::extracted)
          .collect(Collectors.toList());
  list.add("</" + lastTag + ">");
  for (String data : list) {
    System.out.println(data);
  }
}

private static String lastTag = null;

private static String extracted(String path) {
  String[] paths = path.split("\\.");

  String tag = paths[0];
  String node = paths[1];

  String s = "";
  if (!tag.equals(lastTag)) {
    if (lastTag != null) {
      s += "</" + lastTag + ">";
    }
    s += "<" + tag + ">";
  }
  s += "<" + node + ">" + node + "</" + node + ">";

  lastTag = tag;
  return s;
}

我知道我可以将其转换为foreach循环,从而减少hacky,但也许可以使用流来实现。

我会改变几件事。 首先,您的XML转换方法看起来不容易扩展到更复杂的用例。 除非您坚持使用两个嵌套标签,否则将需要一个类,该类可以对Collection API和Stream API都不提供的分层结构进行建模。 以下通用实用程序类可能是一个起点:

public final class Tree<T> {
  T value;
  Map<T,Tree<T>> sub=Collections.emptyMap();
  public Tree(T value) {
    this.value=value;
  }
  public Tree<T> add(T value) {
    if(sub.isEmpty()) sub=new HashMap<>();
    return sub.computeIfAbsent(value, Tree::new);
  }
  public void addAll(Tree<T> tree) {
    if(!tree.sub.isEmpty()) {
      if(sub.isEmpty()) sub=new HashMap<>();
      for(Tree<T> t: tree.sub.values()) add(t.value).addAll(t);
    }
  }
  public <R> R forAll(
    Function<T, R> open, Function<T, R> single, Function<T, R> close,
    BiFunction<R,R,R> combiner) {
      if(sub.isEmpty()) return single.apply(value);
      else {
        Iterator<Tree<T>> it=sub.values().iterator();
        R result = value!=null? open.apply(value):
          it.next().forAll(open, single, close, combiner);
        while(it.hasNext())
          result=combiner.apply(result, it.next().forAll(open,single,close,combiner));
        return value!=null? combiner.apply(result, close.apply(value)): result;
      }
  }
}

故意将其保持为最低限度,并且不限于特定用例。 为了支持从Stream (例如,从路径)生成层次结构,可以使用以下Collector

public final class TreeCollector<T>
    implements Collector<T, TreeCollector<T>, Tree<T>> {
  T value;
  Tree<T> root, current;
  public TreeCollector(T rootValue) {
    value=rootValue;
    current=root=new Tree<>(value);
  }
  public Supplier<TreeCollector<T>> supplier() {
    return ()->new TreeCollector<>(value);
  }
  public BiConsumer<TreeCollector<T>, T> accumulator() {
    return (c,t)->{ c.current=c.current.add(t); };
  }
  public BinaryOperator<TreeCollector<T>> combiner() {
    return (a,b)->{ a.root.addAll(b.root); return a; };
  }
  public Function<TreeCollector<T>, Tree<T>> finisher() {
    return x->x.root;
  }
  public Set<Characteristics> characteristics() {
    return Collections.emptySet();
  }
}

第二件事是,如果您想有效地使用Stream API,则不应使用String.split使用结果数组或String.trim作为已由模式匹配结果进行映射的操作来创建Stream Pattern.splitAsStream允许处理模式匹配结果,而无需将它们存储到中间数组中。

放在一起,用例复制问题代码的结果,如下所示:

Pattern dot = Pattern.compile(".", Pattern.LITERAL);

Tree<String> root=
Pattern.compile("\\s+", Pattern.DOTALL).splitAsStream(input)
       .map(path->dot.splitAsStream(path).collect(new TreeCollector<>(null)))
       .collect(()->new Tree<>(null), Tree::addAll,  Tree::addAll);
String xml=root.forAll(s->'<'+s+'>', s->'<'+s+'>'+s+"</"+s+">\n", s->"</"+s+'>',
                       String::concat);
System.out.println(xml);

尽管以下内容对我来说看起来更自然:

Tree<String> root=
Pattern.compile("\\s+", Pattern.DOTALL).splitAsStream(input)
       .map(path->dot.splitAsStream(path).collect(new TreeCollector<>(null)))
       .collect(()->new Tree<>(null), Tree::addAll,  Tree::addAll);
String xml=root.forAll(s->'<'+s+'>', s->'<'+s+"/>", s->"</"+s+">\n", String::concat);
System.out.println(xml);

它产生

<aaa><xxxx/><zzz/></aaa>
<bbb><cccc/></bbb>

请注意,您可以使用此代码来收集path.strings.of.arbitrary.length。

另一个优点是,构建层次结构时无需对整个流进行排序,如果您具有更多的路径元素,这将是一个很大的好处。


附录

如果您不受原始问题的限制,则可以使用collect操作来创建Map<…,Set<…>>Map<…,List<…>>以建模第二级的层次结构。 在这种情况下,您无需其他类即可解决任务。 有两种方法可以实现此目的,可以使用现有Collectors的组合,也可以通过指定供应商,累加器和合并器功能来创建临时收集器。

合并现有的收集器实现(使用import static java.util.stream.Collectors.*; ):

Pattern dot = Pattern.compile(".", Pattern.LITERAL);
Pattern.compile("\\s+", Pattern.DOTALL).splitAsStream(input)
       .map(path -> dot.split(path, 2))
       .collect(groupingBy(path->path[0],
                    mapping(path->path[1], toCollection(TreeSet::new))))
       .forEach((p,l) -> {
           System.out.print('<'+p+'>');
           for(String s:l) System.out.println('<'+s+'>'+s+"</"+s+'>');
           System.out.print("</"+p+'>');
       });

创建一个临时收集器:

Pattern dot = Pattern.compile(".", Pattern.LITERAL);
Pattern.compile("\\s+", Pattern.DOTALL).splitAsStream(input)
       .map(path -> dot.split(path, 2))
       .collect(() -> new TreeMap<String,Set<String>>(),
                (m,p) -> m.computeIfAbsent(p[0], k->new TreeSet<>()).add(p[1]),
                (m1,m2) -> m2.forEach(
                           (k,v)->m1.computeIfAbsent(k,x->new TreeSet<>()).addAll(v)))
       .forEach((p,l) -> {
           System.out.print('<'+p+'>');
           for(String s:l) System.out.println('<'+s+'>'+s+"</"+s+'>');
           System.out.print("</"+p+'>');
       });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM