我有以下项目清单(订单事项):
<A> <B> [ ] [\n] <C> [ ] [ ] [\n\r] <D> <A> [ ] [ xyz ] [ abc ] <X>
我想合并用方括号表示的文本节点:
<A> <B> [ \n] <C> [ \n\r] <D> <A> [ xyz abc ] <X>
我使用flags和 prevNode 指针编写一个for-each循环来执行任务,但它感觉不对:
我相信 Java 8 Stream API 是关于:编写代码的工作量减少,阅读代码的工作量减少。
Java中是否有类似的机制来处理列表?
更新
鉴于没有现成的解决方案,我已经开发了一个非常简单的流畅的API 来解决任务。减少现在看起来像这样:
List<Node> output = ListStream.of(input)
.continuousRegion(node -> node instanceof TextNode)
.reduceRegion((a, b) -> new TextNode(a.value + b.value))
.toArrayList();
它完美地解决了许多任务,您必须在列表中的连续区域上运行 。我有很多这样的,为进一步的数据处理准备XML。
算法:
public List<Node> reduce(List<Node> list) {
List<Node> result = new ArrayList<>();
TextNode prevTextNode = null;
for (Node node : list) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
if (prevTextNode == null) {
prevTextNode = textNode;
} else {
prevTextNode = new TextNode(prevTextNode.value + textNode.value);
}
} else {
if (prevTextNode != null) {
result.add(prevTextNode);
prevTextNode = null;
}
result.add(node);
}
}
if (prevTextNode != null) {
result.add(prevTextNode);
}
return result;
}
所有代码(可编辑):
import com.sun.deploy.util.StringUtils;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
public class ListReductionExample {
class Node {
final String value;
Node(String value) {
this.value = value;
}
@Override
public String toString() {
return "<" + value + ">";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Node)) return false;
Node node = (Node) o;
return value != null ? value.equals(node.value) : node.value == null;
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
}
class TextNode extends Node {
TextNode(String value) {
super(value);
}
@Override
public String toString() {
return "[" + value + "]";
}
}
public List<Node> reduce(List<Node> list) {
List<Node> result = new ArrayList<>();
TextNode prevTextNode = null;
for (Node node : list) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
if (prevTextNode == null) {
prevTextNode = textNode;
} else {
prevTextNode = new TextNode(prevTextNode.value + textNode.value);
}
} else {
if (prevTextNode != null) {
result.add(prevTextNode);
prevTextNode = null;
}
result.add(node);
}
}
if (prevTextNode != null) {
result.add(prevTextNode);
}
return result;
}
public void printList(List<Node> list) {
List<String> listOfStrings = list.stream().map(Node::toString).collect(Collectors.toList());
System.out.println(StringUtils.join(listOfStrings, " "));
}
@Test
public void test() {
// <A> <B> [ ] [N] <C> [ ] [ ] [NR] <D> <A> [ ] [ xyz ] [ abc ] <X>
List<Node> input = new ArrayList<>();
input.add(new Node("A"));
input.add(new Node("B"));
input.add(new TextNode(" "));
input.add(new TextNode("N"));
input.add(new Node("C"));
input.add(new TextNode(" "));
input.add(new TextNode(" "));
input.add(new TextNode("NR"));
input.add(new Node("D"));
input.add(new Node("A"));
input.add(new TextNode(" "));
input.add(new TextNode(" xyz "));
input.add(new TextNode(" abc "));
input.add(new Node("X"));
printList(input);
// <A> <B> [ N] <C> [ NR] <D> <A> [ xyz abc ] <X>
List<Node> expectedOutput = new ArrayList<>();
expectedOutput.add(new Node("A"));
expectedOutput.add(new Node("B"));
expectedOutput.add(new TextNode(" N"));
expectedOutput.add(new Node("C"));
expectedOutput.add(new TextNode(" NR"));
expectedOutput.add(new Node("D"));
expectedOutput.add(new Node("A"));
expectedOutput.add(new TextNode(" xyz abc "));
expectedOutput.add(new Node("X"));
printList(expectedOutput);
assertEquals(expectedOutput, reduce(input));
}
}
答案 0 :(得分:1)
没有这样的功能只能减少部分流。尝试在Stream API上构建此类功能的所有解决方案都比循环复杂得多。你可以做些什么来改善你的循环变体,是摆脱null
值和相关的条件。一般逻辑如下:
public static <T> List<T> joinElements(
List<T> list, BiPredicate<T,T> p, BinaryOperator<T> join) {
if(list.isEmpty()) return Collections.emptyList();
T element=list.get(0);
int num=list.size();
if(num==1) return Collections.singletonList(element);
List<T> result=new ArrayList<>(num);
for(int ix=1; ix<num; ix++) {
T next=list.get(ix);
if(p.test(element, next)) {
element=join.apply(element, next);
}
else {
result.add(element);
element=next;
}
}
result.add(element);
return result;
}
如果您怀疑调用者提供非随机访问列表(程序员应该避免),您可以使用
public static <T> List<T> joinElements(
List<T> list, BiPredicate<T,T> p, BinaryOperator<T> join) {
Iterator<T> it=list.iterator();
if(!it.hasNext()) return Collections.emptyList();
T element=it.next();
if(!it.hasNext()) return Collections.singletonList(element);
List<T> result=new ArrayList<>();
do {
T next=it.next();
if(p.test(element, next)) {
element=join.apply(element, next);
}
else {
result.add(element);
element=next;
}
} while(it.hasNext());
result.add(element);
return result;
}
这可能像
一样使用List<String> result = joinElements(list,
(a,b) -> isTextNode(a) && isTextNode(b), (a,b) -> new TextNode(a.value+b.value));
为了进行比较,基于流的解决方案可以使用Collector
。由于没有内置的收集器用于这样的任务,我们必须定义一个,它基本上与基于循环的解决方案相同,功能仅分布在多个函数上,由迭代的Stream评估:
List<String> result=list.stream().collect(ArrayList::new,
(l,n) -> l.add(!l.isEmpty() && isTextNode(l.get(l.size()-1)) && isTextNode(n)?
new TextNode(l.remove(l.size()-1).value+n.value): n),
(l1,l2) -> {
if(!l2.isEmpty() && !l1.isEmpty()
&& isTextNode(l1.get(l1.size()-1)) && isTextNode(l2.get(0))) {
l2.set(0, new TextNode(l1.remove(l1.size()-1).value+l2.get(0).value));
}
l1.addAll(l2);
});
决定自己这是否比循环变体更好......
答案 1 :(得分:0)
我强烈感觉你不是在讨论Collection API中的列表,而是讨论DOM节点。在那种情况下,你正在做不必要的工作。看看
将所有
Text
个节点放在此Node
下的子树的完整深度(包括属性节点)中,形成“正常”形式,其中只有结构(例如,元素,注释,处理指令) ,CDATA部分和实体引用)分隔Text
个节点,即既没有相邻的Text
节点也没有空Text
个节点。
结果没有相邻的Text
个节点意味着加入先前相邻的Text
个节点。所以你要做的就是在parant节点上调用normalize()
。