Java 按对象列表分组字段和计数出现次数
我有一个Java 按对象列表分组字段和计数出现次数,java,java-stream,Java,Java Stream,我有一个列表,它包含像+或*这样的操作的所有操作数。也就是说,它不是二叉树结构,而是每个节点可能包含多个子节点的树。术语节点可以是变量、运算符、函数或数字。一个数字和一个变量节点包含一个空的子项列表(将子项视为操作的参数) 在simpleoperator方法的某个地方,我想用相同的值折叠变量。对于上面的表达式,树如下所示: _______ OPERATOR _______ / "+" \
列表
,它包含像+
或*
这样的操作的所有操作数。也就是说,它不是二叉树结构,而是每个节点可能包含多个子节点的树。术语节点可以是变量、运算符、函数或数字。一个数字和一个变量节点包含一个空的子项列表(将子项视为操作的参数)
simpleoperator
方法的某个地方,我想用相同的值折叠变量。对于上面的表达式,树如下所示:
_______ OPERATOR _______
/ "+" \
/ / \ \
VARIABLE VARIABLE VARIABLE VARIABLE
"x" "x" "x" "x"
children.stream()
.collect(Collector.toMap(
Function.identity(),
Collectors.summingInt(x -> 1),
(left, right) -> {
return left + right;// or left + 1
}
() -> new TreeMap<>(Comparator.comparing(TermNode::toCompareBy))
))
树节点
中的子节点
字段,将此树转换为更简单的树:此表达式由一个运算符
TermNode和运算符+以及子节点列表组成,其中包含一个变量
TermNode和值x
四次(不是同一个TermNode的四次,只有四个TermNode具有完全相同的值、子项和类型。下面是我遇到的问题:
private TermNode rearrangeTerm(Operator operator, List<TermNode> children) {
Map<TermNode, Integer> occurrences = children.stream()
.collect(Collectors.groupingBy(term -> term, Collectors.summingInt(term -> 1)));
List<Pair<TermNode, Integer>> simplification = occurrences.entrySet().stream()
.map(entry -> new Pair<TermNode, Integer>(entry.getKey(), entry.getValue())).collect(Collectors.toList());
List<TermNode> rearranged = simplification.stream().map(pair -> rearrangeTerm(operator, pair)).collect(Collectors.toList());
return new TermNode(operator.getOperator(), rearranged, TermTypeEnum.OPERATOR);
}
专用术语节点重新排列术语(运算符,列出子项){
映射出现次数=children.stream()
.collect(Collectors.groupby(term->term,Collectors.summingit(term->1));
列表简化=引用。entrySet().stream()
.map(entry->new Pair(entry.getKey(),entry.getValue()).collect(Collectors.toList());
重新排列的列表=simplify.stream().map(对->重新排列项(运算符,对)).collect(收集器.toList());
返回新的TermNode(operator.getOperator(),重新排列,TermTypeEnum.operator);
}
调用此方法时,将参数重新排列术语(运算符,子项)
传递给方法,其中运算符是我们的+
运算符,子项是我们的TermNode
元素列表,其中包含变量x
四次
此时,term->term
收集器。groupingBy
不按字段值(值、子项和类型)对TermNode元素进行分组,而是作为TermNode引用本身。因此问题是,如何按字段对TermNode
元素进行分组(如果两个TermNode
元素的所有字段都匹配,则它们相等(子列表中元素的顺序可能不同,但重要的是每个TermNode
的子列表中元素的出现情况匹配。当然TermNode
类型也必须匹配。这里的问题只是如何更改重新排列term
方法,以便映射对于表达式x+x+x+x+x
,只包含一个带有(“x”,4)的条目这将是太长的评论在这里,所以张贴作为一个答案 因此,一般的想法是,您需要能够判断两个
TermNode
s对象是否相同;为此,您可以向对象添加一个方法,该方法将展平对象,并以某种方式从其每个字段递归计算一个String
,并按字母数字排序。因为您的对象仅由两个字段组成ds:value
和TermTypeEnum
这并不难实现,类似的内容可能是(显然没有编译):
私有列表展平;
私有列表展平(TermNode节点){
if(node.getChildren()>0){
//这里有一些递归
}否则{
//这里可能有支票
展平.add(node.value+node.type)
}
}
//这将为您提供一个从所有值中排序的字符串
用于比较的公共字符串(){
返回flatned.sorted()。。。
}
所以你的代码是:
Map<TermNode, Integer> occurrences = children.stream()
.collect(Collectors.groupingBy(
term -> term,
Collectors.summingInt(term -> 1)));
Map引用数=children.stream()
.collect(收集器.groupingBy(
术语->术语,
收集器。总和(术语->1));
可能会变成这样:
_______ OPERATOR _______
/ "+" \
/ / \ \
VARIABLE VARIABLE VARIABLE VARIABLE
"x" "x" "x" "x"
children.stream()
.collect(Collector.toMap(
Function.identity(),
Collectors.summingInt(x -> 1),
(left, right) -> {
return left + right;// or left + 1
}
() -> new TreeMap<>(Comparator.comparing(TermNode::toCompareBy))
))
children.stream()
.collect(Collector.toMap(
Function.identity(),
收集器。总和(x->1),
(左,右)->{
返回left+right;//或left+1
}
()->新树映射(Comparator.comparing(TermNode::toCompareBy))
))
谢谢你的回答,我最终不得不重写我的TermNode
类的equals(objectobj)
和hashCode()
我跟踪了多个stackoverflow站点并使用了以下方法:
@Override
public boolean equals(Object other) {
if(this == other) {
return true;
}
if((other == null) || (other.getClass() != this.getClass())) {
return false;
}
TermNode node = (TermNode)other;
return this.value.equals(node.getValue())
&& PlotterUtil.isEqual(this.children, node.getChildren())
&& this.type == node.getType();
}
由于TermNode
类包含一个List
字段,因此需要检查参数列表是否也相等,但忽略参数的顺序,因为这些参数要么是单个值(如数字或单个变量),要么是包含在另一个TermNode
中(如运算符)e> 函数
永远不能忽略参数的顺序。为了检查两个列表是否相等,我使用了下面的代码
publicstaticbooleaniesequal(列表一,列表二){
if(一==null&&two==null){
返回true;
}
if((一!=null&&two==null)| |(一==null&&two!=null)){
返回false;
}
如果(1.size()!=2.size()){
返回false;
}
一个=getSortedList(一个);
两个=getSortedList(两个);
返回1。等于(2);
}
这也解决了这个问题
Map<TermNode, Integer> occurrences =
children.stream().collect(Collectors
.groupingBy(term -> term, Collectors.summingInt(term -> 1))
);
Map引用=
children.stream().collect(收集器
.groupingBy(术语->术语,收集器.summingit(术语->1))
);
因为
Map
通过equals(Object obj)
和对象的hashCode()
方法检查term->term
中的相等键。您能提供一个代数表达式,以及您希望它生成的成对列表吗?与相对应的代数表达式是什么
public static boolean isEqual(List<TermNode> one, List<TermNode> two) {
if(one == null && two == null) {
return true;
}
if((one != null && two == null) || (one == null && two != null)) {
return false;
}
if(one.size() != two.size()) {
return false;
}
one = getSortedList(one);
two = getSortedList(two);
return one.equals(two);
}
Map<TermNode, Integer> occurrences =
children.stream().collect(Collectors
.groupingBy(term -> term, Collectors.summingInt(term -> 1))
);