【问题标题】:How do I find cycles in my object hierarchy?如何在我的对象层次结构中找到循环?
【发布时间】:2015-08-10 07:30:56
【问题描述】:

有一个类Company,它引用了Company 的另一个实例来表示parent。假设有四家公司c1c2c3 & c4c2c3c4 的母公司设置为c1

例如:

public class Company {
  public Company parent;

  public Company() { }
  public Company(Company parent) {
    this.parent = parent;
  }

  public static void main(String[] args) {
    Company c1 = new Company();
    Company c2 = new Company(c1);
    Company c3 = new Company(c1);
    Company c4 = new Company(c1);
}

如果我们将c2 设置为c1 的母公司:

c1.parent = c2;

然后它将在公司层次结构中创建一个Cyclomatic Complexity 无限循环,我们必须在我们的系统中避免这种循环。

我们希望能够在运行时检测到这一点。在上述情况下,检查同一类对象的圈复杂度的最佳算法是什么?

【问题讨论】:

    标签: java algorithm cyclomatic-complexity object-graph


    【解决方案1】:

    您的任务与圈复杂度无关。您的实体基本上形成了一个图表,您想要检测其中的循环。常见的方法是执行DFS

    你可以找到很多例子over the internet

    【讨论】:

      【解决方案2】:

      我已经以编程方式解决了这个问题,为已处理对象创建了一个本地 Set,并让它在每次调用递归方法时作为输入参数传播。

      例如:

      public void myMethod(Company c)
      {
          Set<Company> visitedCompanies=new HashSet<Company>();
          visitedCompanies.add(c);
          myPrivateMethod(c, visitedCompanies);
      }
      
      private void myPrivateMethod(Company c, Set<Company> visitedCompanies)
      {
          if (visitedCompanies.contains(c))
          {
              // Cylce detected
          }
          else
          {
              //... do some stuff
      
              // Go upwards in the hierarchy
              if (c.getParent()!=null)
              {
                  visitedCompanies.add(c);
                  myPrivateMethod(c.getParent(), visitedCompanies);
              }
          }
      

      当然,您必须首先确保您的类 Company 是可索引的:它正确地覆盖了 hashCodeequals

      请注意,这个算法甚至可以在 Company 抽象之外实现(如本例所示),因为它在每次调用中将 Company 对象作为遍历状态的一部分(与集合一起)传播。这不是强制性的,因为这些方法本身可能是 Company 抽象的一部分,但 必须将该集合作为输入参数传播。

      【讨论】:

        【解决方案3】:

        您可以将parent 设置为private 并使用setParent(Company) 方法更改parent 的值。那么:

        public boolean setParent(Company parent) {
            Company curr = parent;
            while (curr != null) {
                if (curr.equals(this)) {
                    return false; // Failed as cycle
                } else {
                    curr = curr.getParent();
                }
            }
            this.parent = parent;
            return true;
        }
        

        无论如何,使用 public 变量通常是不好的做法,因为它会破坏封装。

        如果您无法将字段更改为private,则:

        public List<Company> hasCycle(List<Company> companies) {
            while (companies.size() > 0) {
                List<Company> cycle = new ArrayList<Company>();
                Company curr = companies.get(companies.length() - 1);
                cycle.add(curr);
                while (curr.parent != null) {
                    curr = curr.parent;
                    if (cycle.contains(curr)) {
                        return cycle; // Cycle detected
                    }
                    cycle.add(curr);
                }
                companies.removeAll(cycle); // Remove all elements we traversed through just now
            }
            return null;
        }
        

        编辑:将hasCycle 的返回更改为返回一个List&lt;Company&gt;,其中包含一个循环中的所有公司以供进一步处理(打印出来、删除它们等)。

        【讨论】:

        • 这个方法最好写成抛出异常
        猜你喜欢
        • 2013-01-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-13
        • 2022-01-15
        相关资源
        最近更新 更多