根据子值填充父List元素

时间:2012-02-15 08:55:41

标签: java list loops recursion arraylist

请考虑以下代码:

CLASS 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 Tester

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);
            }
        }
    }
}

运行此操作时,您将注意到列表中父元素的值将使用子列表中值的总和进行更新。

我面临的问题是我想让它一直更新到树而不仅仅是直接的父级。

是否有一种模式可以帮助我实现这一目标?

见下图:

enter image description here

4 个答案:

答案 0 :(得分:4)

像其他人一样建议我使用Observer模式。每个父节点都会侦听子节点的更改。

但是我的解决方案与@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.您将获得对parent.updateNode(3)的2次调用,并且每个父级都将更新。

答案 1 :(得分:2)

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);
         }
    }
}

答案 2 :(得分:1)

其他海报建议使用Observer pattern。观察者模式是Pub/Sub pattern的子集。我建议通过观察者模式使用它。

观察者模式和发布/订阅模式之间的主要区别在于,在观察者模式中,观察者既是ChangeEvents的发布者,也是消息的调度程序。它实质上是将每个Observable都变成了EventDispatcher。在传统的Pub / Sub模式中,Observables只是ChangeEvents的发布者。 ChangeEvents发布到一个单独的EventDispatchingService,它处理事件需要发送到的订阅者。

尝试使用Observer模式跟踪全局更改很难做到。例如,如果要计算调用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());

答案 3 :(得分:0)

根据您的班级名称,我想您希望在运行时看到您的审核进度。所以我的假设是:

  • 树结构变化不大,创建后几乎固定
  • 节点值经常变化,计数器初始状态为0

这是一个有效的实施方案:

  • 每个节点都维护其父节点的完整列表
  • 节点插入0值
  • 当节点值更改或仅增加时,通过应用先前节点值之间的增量来更新节点列表中的父项值

因此,结构始终是最新的,节点插入仍然是可能的,并且不会影响现有节点。

如果许多审计线程同时运行并将值报告到结构中,则必须注意并发问题并使用AtomicInteger作为计数器持有者。

这是一个务实的设计,我真的没有找到任何匹配的模式。与排序算法一样,尝试在这样的上下文中使用模式可能会适得其反。