根据子值填充父 List 元素
Posted
技术标签:
【中文标题】根据子值填充父 List 元素【英文标题】:Populate parent List elements based on child values 【发布时间】:2012-03-06 14:46:43 【问题描述】:考虑以下代码:
类AuditProgressReport:
public class AuditProgressReport
private List<AuditProgressReport> audit_progress_reports = null;
private String name = null;
private String description = null;
private int compliant;
private int non_compliant;
private int not_completed ;
/**
*
*/
public AuditProgressReport()
super();
public AuditProgressReport(
String name_param,
int compliant_param,
int non_compliant_param,
int not_completed_param)
super();
this.name = name_param;
this.compliant = compliant_param;
this.non_compliant = non_compliant_param;
this.not_completed = not_completed_param;
public void addToCompliant(int compl_to_add_param)
this.compliant += compl_to_add_param;
public void addToNonCompliant(int non_compl_to_add_param)
this.non_compliant += non_compl_to_add_param;
public void addToNotCompleted(int not_compl_param)
this.not_completed += not_compl_param;
public void setAuditProgressReports(List<AuditProgressReport> report_category_nodes_param)
this.audit_progress_reports = report_category_nodes_param;
public List<AuditProgressReport> getAuditProgressReports()
return this.audit_progress_reports;
public void setCompliant(int compliantParam)
this.compliant = compliantParam;
public int getCompliant()
return this.compliant;
public void setNonCompliant(int nonCompliantParam)
this.non_compliant = nonCompliantParam;
public int getNonCompliant()
return this.non_compliant;
public void setNotCompleted(int notCompletedParam)
this.not_completed = notCompletedParam;
public int getNotCompleted()
return this.not_completed;
public void setName(String name_param)
this.name = name_param;
public String getName()
return this.name;
public void setDescription(String description_param)
this.description = description_param;
public String getDescription()
return this.description;
@Override
public String toString()
return ("Compliant["+this.compliant+
"] Non-Compliant["+this.non_compliant+
"] Not-Completed["+this.not_completed+"]");
还有 CLASS 测试员:
public class Tester
public static void main(String[] args)
List<AuditProgressReport> main_level = new ArrayList<AuditProgressReport>();
AuditProgressReport ar_1_1 = new AuditProgressReport("ar_1_1",0,0,0);
AuditProgressReport ar_1_2 = new AuditProgressReport("ar_1_2",0,0,0);
AuditProgressReport ar_1_1_1 = new AuditProgressReport("ar_1_1_1",0,0,0);
AuditProgressReport ar_1_1_2 = new AuditProgressReport("ar_1_1_2",15,65,20);
AuditProgressReport ar_1_1_3 = new AuditProgressReport("ar_1_1_3",20,30,50);
AuditProgressReport ar_1_1_1_1 = new AuditProgressReport("ar_1_1_1_1",5,5,90);
AuditProgressReport ar_1_1_1_2 = new AuditProgressReport("ar_1_1_1_2",55,5,40);
AuditProgressReport ar_1_1_1_3 = new AuditProgressReport("ar_1_1_1_3",35,35,30);
List<AuditProgressReport> arl_1_1_1 = new ArrayList<AuditProgressReport>();
arl_1_1_1.add(ar_1_1_1_1);
arl_1_1_1.add(ar_1_1_1_2);
arl_1_1_1.add(ar_1_1_1_3);
ar_1_1_1.setAuditProgressReports(arl_1_1_1);
List<AuditProgressReport> arl_1_1 = new ArrayList<AuditProgressReport>();
arl_1_1.add(ar_1_1_1);
arl_1_1.add(ar_1_1_2);
arl_1_1.add(ar_1_1_3);
AuditProgressReport ar_1_2_1 = new AuditProgressReport("ar_1_2_1",10,30,60);
AuditProgressReport ar_1_2_2 = new AuditProgressReport("ar_1_2_2",20,20,60);
List<AuditProgressReport> arl_1_2 = new ArrayList<AuditProgressReport>();
arl_1_2.add(ar_1_2_1);
arl_1_2.add(ar_1_2_2);
ar_1_1.setAuditProgressReports(arl_1_1);
ar_1_2.setAuditProgressReports(arl_1_2);
main_level.add(ar_1_1);
main_level.add(ar_1_2);
Tester tester = new Tester();
for(AuditProgressReport prog_rep : main_level)
tester.populateParents(prog_rep, null);
//TODO Now check the values...
private void populateParents(
AuditProgressReport audit_progress_param,
AuditProgressReport parent_param)
List<AuditProgressReport> audit_progress =
audit_progress_param.getAuditProgressReports();
System.out.println("name["+audit_progress_param.getName()+"]");
if(parent_param != null)
int compl = audit_progress_param.getCompliant();
int nonCompl = audit_progress_param.getNonCompliant();
int notCompleted = audit_progress_param.getNotCompleted();
parent_param.addToCompliant(compl);
parent_param.addToNonCompliant(nonCompl);
parent_param.addToNotCompleted(notCompleted);
if(audit_progress != null && ! audit_progress.isEmpty())
for(AuditProgressReport prog_rep : audit_progress)
this.populateParents(prog_rep,audit_progress_param);
当您运行此程序时,您会注意到列表中父元素的值会更新为子列表中值的总和。
我面临的问题是我希望它在整个树中一直更新,而不仅仅是直接父级。
有没有一种模式可以帮助我实现这一目标?
见下图:
【问题讨论】:
对于每个节点,将其值设置为其子节点值的总和。闻起来像递归;) 你不能让每个父母都听听它的孩子吗?还可以为您节省手动更新的一些麻烦。 【参考方案1】:根据您的班级名称,我猜您希望在运行时实时查看审核进度。所以我的假设:
树形结构变化不大,创建后基本固定 节点值经常变化,计数器初始状态为 0这是一个有效的实现:
每个节点都维护其父节点的完整列表 节点以 0 值插入 当一个节点值改变或简单地增加时,通过应用前一个节点值之间的增量来更新节点列表中父节点的值因此结构始终是最新的,节点插入仍然是可能的并且不会影响现有节点。
如果许多审计线程同时运行并将值报告到结构中,您必须注意并发问题并使用AtomicInteger
作为计数器持有者。
这是一个务实的设计,真诚地我没有找到任何匹配的模式。与排序算法一样,在这种情况下尝试使用模式可能会适得其反。
【讨论】:
【参考方案2】:其他发帖人建议使用Observer pattern。观察者模式是Pub/Sub pattern 的子集。我建议在观察者模式上使用它。
Observer 模式和 Pub/Sub 模式的主要区别在于,在 Observer 模式中,Observer 既是 ChangeEvents 的发布者,也是消息的分发者。它本质上是将每个 Observable 变成一个 EventDispatcher。在传统的 Pub/Sub 模式中,Observables 只是 ChangeEvents 的发布者。 ChangeEvents 被发布到一个单独的 EventDispatchingService 中,该服务处理事件需要发送到的订阅者。
尝试使用观察者模式来跟踪全局变化是很困难的。例如,如果要计算调用addToCompliant()
方法的次数,则必须在 Observable 的每个实例上添加 Observer。使用 Event Pub/Sub,您的观察者类可以订阅以侦听 ChangeEvent 的类型,并且它将接收所有这些。我用过的最好的(恕我直言)事件发布/订阅库是Google Guava's Event Bus。在您的特定情况下,我会执行以下操作。
public class EventBusSingleton
public static final EventBus INSTANCE = new EventBus("My Event Bus");
public class ComplianceChange
private AuditProgressReport changedReport;
private int delta;
public ComplianceChange(AuditProgressReport changedReport, int delta)
this.changedReport = changedReport;
this.delta = delta;
...
public class AuditProgressReport
...
private AuditProgressReport parent;
public AuditProgressReport getParent()
return parent;
public void addToCompliant(int delta)
this.compliant += delta;
ComplianceChange change = new ComplianceChange(this, delta);
EventBusSingleton.INSTANCE.post(change);
...
public class ComplianceChangeHandler
@Subscribe
public void notifyParent(ComplianceChange event)
AuditProgressReport parent = event.getChangedReport().getParent();
int delta = event.getDelta();
parent.addToCompliant(delta);
@Subscribe
public void somethingElse(ComplianceChange event)
// Do Something Else
// Somewhere during initialization
EventBusSingleton.INSTANCE.register(new ComplianceChangeHandler());
【讨论】:
我认为 Pub/Sub 模式不是解决这个问题的最佳选择,因为问题太简单了,无法使用这种模式。在这个问题中,只有直接父节点想知道其子节点值的变化。对于更复杂的问题,我会使用 Pub/Sub 模式,即两个不同包上的两个不相关类之间的异步通信。事实上,有了这个包,通信的两个部分在编译时或运行时是不知道彼此的。【参考方案3】:像其他人建议的那样,我会使用观察者模式。每个父节点监听子节点的变化。
但我的解决方案与 @zmf 的不同,因为如果您有一棵有很多子节点的大树,并且每次更新时您必须对每个值求和,您将花费大量处理时间。
如果每次更新子节点时只发送旧值和新值之间的差值会怎样。让我们举个例子。你从这棵树开始:
[12]--+--[10]-----[10]
|
+--[ 2]--+--[ ]
|
+--[ 2]
然后你像这样更新一个孩子
[12]--+--[10]-----[10]
|
+--[ 2]--+--[ 3]
|
+--[ 2]
使用值“3”更新的节点通过调用 parent.updateNode(3) 方法将其更改发送给父节点。父节点只需将其当前值(在本例中为“2”)与其从子节点接收的值相加。所以它将更新为值“5”
[12]--+--[10]-----[10]
|
+--[ 5]--+--[ 3]
|
+--[ 2]
新值为“5”的节点会调用parent.updateNode(3),最终的解决方案是
[15]--+--[10]-----[10]
|
+--[ 5]--+--[ 3]
|
+--[ 2]
恕我直言,此解决方案更好,因为每个 updateNode() 方法只需将其自己的当前值与从其子节点接收到的更改相加,并使用接收到的相同值调用其父级。您不必从每个孩子那里获取价值并将所有价值相加。如果您有一棵大树,这将为您节省大量时间。因此,在本例中,当您将值从 0 更改为 3 时。您将获得 2 次对 parent.updateNode(3) 的调用,并且每个父项都会得到更新。
【讨论】:
【参考方案4】:public void updateNode(int value)
if (value != this.value)
this.value = value;
if (getParent() != null)
int sum = 0;
for (Node n : getParent().getChildren())
sum += n.getValue();
getParent.updateNode(sum);
【讨论】:
以上是关于根据子值填充父 List 元素的主要内容,如果未能解决你的问题,请参考以下文章