深层复制的性能影响

时间:2017-11-22 06:26:40

标签: java clone deep-copy

我有一个复杂的对象,它保存在内存中,不应该被修改。它用作模板,其中一些字段在首次实例化时初始化,一些字段在运行时根据一些调用代码参数确定和更新。

考虑这个例子:

FooTemplate是一个加载到内存中的对象。调用代码需要此对象的更新版本。更新后的版本有一个填充的服务中心列表。列表的内容取决于客户端代码中的一些参数,在这种情况下是调用者的国家。

public class FooTemplate {

    private final String companyName;

    private final String identifier;

    private final List<String> serviceCenters = new ArrayList<String>();

    public FooTemplate(String companyName, String identifier) {
        this.companyName = companyName;
        this.identifier = identifier;
    }

    public String getCompanyName() {
        return companyName;
    }

    public String getIdentifier() {
        return identifier;
    }

    public List<String> getServiceCenters() {
        return serviceCenters;
    }

    @Override
    public String toString() {
        return "FooTemplate [companyName=" + companyName + ", identifier="
                + identifier + ", serviceCenters=" + serviceCenters + "]";
    }       

}

FooManager是一个加载FooTemplate对象的单例:

public class FooManager {

    private static final FooManager fooManager = new FooManager();

    private FooTemplate fooTemplate;

    private FooManager() {      
        this.fooTemplate = new FooTemplate("ABC inc.", "93746789");
    }

    public static FooManager getInstance() {
        return fooManager;
    }

    public FooTemplate getFoo() {
        return fooTemplate;
    }

}

这是测试类:

   class Test {

    public static void main(String[] args) {

        Caller[] callers = {new Caller (CountryCode.US),
                new Caller (CountryCode.US),
                new Caller (CountryCode.US)};

        for (int i = 0; i < callers.length; i++){
        System.out.println(getFoo(callers[i]));
        }

    }

    public static FooTemplate getFoo(Caller caller) {

        FooManager fooManager = FooManager.getInstance();

        FooTemplate fooTemplate = fooManager.getFoo();

        if (caller.getCountryCode() == CountryCode.US){ 
            fooTemplate.getServiceCenters().add("Seattle");
            fooTemplate.getServiceCenters().add("Boston");
        }
        if (caller.getCountryCode() == CountryCode.GER){    
            fooTemplate.getServiceCenters().add("Munich");
            fooTemplate.getServiceCenters().add("Berlin");
        }
        if (caller.getCountryCode() == CountryCode.FRA){    
            fooTemplate.getServiceCenters().add("Paris");
            fooTemplate.getServiceCenters().add("Lyon");
        }   

        return fooTemplate;

    }
}

它产生以下输出:

FooTemplate [companyName = ABC inc。,identifier = 93746789,serviceCenters = [Seattle,Boston]]

FooTemplate [companyName = ABC inc。,identifier = 93746789,serviceCenters = [Seattle,Boston,Seattle,Boston]]

FooTemplate [companyName = ABC inc。,identifier = 93746789,serviceCenters = [Seattle,Boston,Seattle,Boston,Seattle,Boston]]

我理解这种情况正在发生,因为每次迭代都会修改相同的对象。我是否需要在修改对象之前对其进行深层复制并返回副本,还是有更好的解决方案?

编辑: 在实际代码中,模板对象是通过解析xml创建的,解析只在单例中完成一次。这就是我每次需要时都无法创建新模板对象的原因。

2 个答案:

答案 0 :(得分:0)

每次拨打FooTemplate时,您都可以创建getFoo的新实例。

public static FooTemplate getFoo() {
    return new FooTemplate("ABC inc.", "93746789");
}

或者,如果您不想创建过多companyNameidentifier,请考虑致电String.intern()。 (我认为编译器无论如何都会优化内存分配。)

如果两个字符串在整个程序中都是常量,请考虑将它们声明为static final

一般情况下,从头开始创建对象时,从另一个对象复制对象并不是一个好习惯。

编辑: 只需创建一个新类FooCompany

public class FooCompany {

    private final String companyName;

    private final String identifier;

    public FooCompany(String companyName, String identifier) {
        this.companyName = companyName;
        this.identifier = identifier;
    }

    public String getCompanyName() {
        return companyName;
    }

    public String getIdentifier() {
        return identifier;
    }
}


public class FooManager {

    private static final FooManager fooManager = new FooManager();

    private FooCompany fooCompany;

    private FooManager() {      
        this.fooCompany = new FooCompany("ABC inc.", "93746789");
    }

    public static FooManager getInstance() {
        return fooManager;
    }

    public FooTemplate getFoo() {
        return new FooTemplate(fooCompany.getCompanyName(), fooCompany.getIdentifier());
    }

}

答案 1 :(得分:0)

//您可以使用以下代码替换FooManger

public class FooManager {

private static final FooManager fooManager = new FooManager();
//if company and contact are fix, make them final
private final String COMPANY="ABC inc.";
private final String CONTACT="93746789";

private FooManager() {      

}

public static FooManager getInstance() {
    return fooManager;
}

public FooTemplate getFoo() {
    return new FooTemplate(COMPANY, CONTACT);
}

}