避免多级循环嵌套

时间:2012-11-17 23:55:27

标签: java

我有一个像这样的对象图:

root
    : childs (array)
        : childs (array)

我正在构建一个JSON响应,因此我需要循环遍历每个集合,创建如下代码:

// code for root

// loop through direct root childs
for (Child child : childs) {

    // Loop through the childs of the object in current context.
    for (AnotherChild anotherChild : moreChilds) {

    }
}

你如何避免这样的代码?最后它将是一个箭头。我可以为每个for循环级别创建自己的方法,但这是一个好方法吗?还有其他更好的方法吗?

4 个答案:

答案 0 :(得分:3)

如果我们正在讨论这个特定问题(构建JSON响应),您可以使用某种类型的序列化程序(如jackson)或编写自定义序列化程序。有关此主题的相关问题https://stackoverflow.com/questions/338586/a-better-java-json-library

另一方面,对于其他一些用途,您可以使用更实用的方法,例如GuavaLambdaj

但是当它对大O复杂性做出时,这些并没有多大的帮助,所以如果可能的话,你可能想尝试不同的方法。

答案 1 :(得分:2)

这是一个递归结构,然后你应该使用递归来处理嵌套。首次深度访问应该是。

编辑以接口JSON,你真的会按照@Mite Mitreski的建议,进行递归访问伪代码样本:

void visit(Child tree) {
  json_write_class(tree);
  for (Attribute a : tree.attributes) {
    json_write_attr(a);
  if (tree.children != null) {
    json_push_indent();
    for (Child child : tree.children) {
      visit(child);
    }
    json_pop_indent();
  }
}

如果您需要更多控制,您可以在该树的节点上编写一些“语义操作”来建立属性,并实现访问者模式以输出数据(比第一个替代方案更冗长)。

经常有助于使用语法和语法树的类比,这些是我们(作为程序员)习惯的最明显的样本。

答案 2 :(得分:1)

我认为你有一个讨厌的设计问题,因为正在做所有这些循环的类知道其他类的很多东西(从而破坏了Law of Demeter)。

我尝试使用的方法(我从一些非常有经验的开发人员那里学到)是将集合(或数组)包装在自己的类中;然后创建迭代数组/集合执行一个操作的方法。在这种情况下,它可能在另一个包装集合的类中调用另一个方法。

通过这种方式,每个类对其他类所做的事情(或子对象的内部)知之甚少。


修改

这是一个例子。想象一下,您在类似亚马逊的网站上拥有一个帐户。在该帐户中,您已关联了几张信用卡。

所以,而不是

class Account {
    List<CreditCard> creditCards;

    public CreditCard getPrimaryCard() {
        //complex code to find the primary credit card
    }
    //lots of other code related to the account and credit cards
}

你可以做到

class Account {
    CreditCards creditCards;

    public CreditCard getPrimaryCard() {
        creditCards.getPrimaryCard()
    }
    //lots of other code related to the account
}

class CreditCards {
    List<CreditCard> creditCards;

    public CreditCard getPrimaryCard() {
        //complex code to find the primary credit card
    }
    public void addCard(CreditCard creditCard) {
        //complex logic to validate that the card is not duplicated.
    }
    //lots of other code related to credit cards
}

通过这种方式,Account不需要知道信用卡如何存储在内存中(如果它是一个列表?还是一组?或者从远程Web服务获取它?)

请记住,这是一个微不足道的例子。

答案 3 :(得分:1)

您可以提供所有感兴趣的类应该实现的接口。该接口应该提供将当前对象转换为JSON的方法。见例:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JsonProgram {

    public static void main(String[] args) {
        Root root = new Root(Arrays.asList(new Child(Arrays.asList(
                new AnotherChild(1), new AnotherChild(2)))));
        System.out.println(root.toJSON());
    }

}

interface JsonState {
    String toJSON();
}

class Root implements JsonState {

    private List<Child> childs = new ArrayList<Child>();

    public Root(List<Child> childs) {
        this.childs = childs;
    }

    @Override
    public String toJSON() {
        StringBuilder builder = new StringBuilder();
        builder.append("{").append("\"childs\"").append(":[");
        int index = 0;
        for (Child child : childs) {
            builder.append(child.toJSON());
            if (index < childs.size() - 1) {
                builder.append(",");
            }
            index++;
        }
        builder.append("]\"}");
        return builder.toString();
    }
}

class Child implements JsonState {

    private List<AnotherChild> anotherChilds = new ArrayList<AnotherChild>();

    public Child(List<AnotherChild> anotherChilds) {
        this.anotherChilds = anotherChilds;
    }

    @Override
    public String toJSON() {
        StringBuilder builder = new StringBuilder();
        builder.append("{").append("\"anotherChilds\"").append(":[");
        int index = 0;
        for (AnotherChild child : anotherChilds) {
            builder.append(child.toJSON());
            if (index < anotherChilds.size() - 1) {
                builder.append(",");
            }
            index++;
        }
        builder.append("]}");
        return builder.toString();
    }
}

class AnotherChild implements JsonState {

    private int value;

    public AnotherChild(int value) {
        this.value = value;
    }

    @Override
    public String toJSON() {
        StringBuilder builder = new StringBuilder();
        builder.append("{").append("\"value\"").append(":\"").append(value)
                .append("\"}");
        return builder.toString();
    }
}

输出:

{
   "childs":[
      {
         "anotherChilds":[
            {
               "value":"1"
            },
            {
               "value":"2"
            }
         ]
      }
   ]
}

但这不是一个好的解决方案。而不是实现您自己的解决方案您应该使用一些可以为您执行此操作的库。我向你推荐google-gson。对我来说是最好的。

编辑 - GSON示例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class JsonProgram {

    public static void main(String[] args) {
        Root root = new Root(Arrays.asList(new Child(Arrays.asList(
                new AnotherChild(1), new AnotherChild(2)))));

        Gson gson = new GsonBuilder().serializeNulls().create();
        System.out.println(gson.toJson(root));
    }
}

class Root {

    private List<Child> childs = new ArrayList<Child>();

    public Root(List<Child> childs) {
        this.childs = childs;
    }

    @Override
    public String toString() {
        return Arrays.toString(childs.toArray());
    }
}

class Child {

    private List<AnotherChild> anotherChilds = new ArrayList<AnotherChild>();

    public Child(List<AnotherChild> anotherChilds) {
        this.anotherChilds = anotherChilds;
    }

    @Override
    public String toString() {
        return Arrays.toString(anotherChilds.toArray());
    }
}

class AnotherChild {

    private int value;

    public AnotherChild(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

上面的示例创建相同的输出。对我来说,这是一个更优雅的解决方案。