[英]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.