【问题标题】:ArrayList objects are overriddenArrayList 对象被覆盖
【发布时间】:2017-05-17 15:58:01
【问题描述】:

在这里尝试使用遗传算法,但我无法弄清楚为什么同一个数组在几行之后返回不同的结果。

这是代码:

Population(int PopulationSize, ArrayList<Vessel> vessels) {
    this.PopulationSize = PopulationSize;
    solutions = new ArrayList(PopulationSize);

    for (int i = 0; i < PopulationSize; i++) {
            Solution s = new Solution(vessels);   
            solutions.add(s);
            System.out.println(i+" : "+solutions.get(i).getFitness());              
    }

    for (int i = 0; i < solutions.size(); i++) {
        System.out.println(solutions.get(i).getFitness());

    }
}

这是输出:

0 : 4432.4956
1 : 2673.922
2 : 5166.998
3 : 4396.5454
4 : 5687.8555
5 : 2907.695
6 : 5005.9937
7 : 3289.161
8 : 3302.1948
9 : 5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338
5193.338

更多源代码。`

Solution.java

package geneticalgorithm;

import static java.lang.Math.abs;
import java.util.ArrayList;

public class Solution {

    ArrayList<Vessel> vessels;

    public Solution(ArrayList<Vessel> vessels) {
        this.vessels = new ArrayList<>(vessels);
        generateSolution();
    }

    public void generateSolution() {    
        do {
            for (Vessel vessel : vessels) {
                vessel.generateParams();
            }
        } while(!isValid());
    }

    /* Getters */
    public Vessel getIndividual(int index) {
        return vessels.get(index);
    }

    // Save individual
    public void saveIndividual(int index, Vessel v) {
        vessels.set(index, v);
    }

    public float getFitness() {
        float fitness = 0, BPCost = 0, CHDelay=0, CBDelay=0;

        for (Vessel v : vessels) {
            BPCost += v.DistancePenaltyCost*abs(v.X-v.LowestCostBerthing);
            CHDelay +=  v.TimePenaltyCost*(abs(2*(v.Y + v.HandlingTime - v.SailingTime))/2);
            CBDelay += v.BunkeringDelayCost*v.BunkeringNeeded*(abs(2*(v.BA+v.BunkeringProcessing-v.SailingTime))/2);
        }
        fitness = BPCost + CHDelay + CBDelay;
        return fitness;
    }

    public boolean isValid() {
        boolean XOverlaps, YOverlaps;

        for (Vessel vTemp : vessels) {
            for (Vessel v : vessels) {
                // Initialization 
                    XOverlaps = YOverlaps = false;

                // Checking for X overlaps
                if(v.X > vTemp.X && v.X < (vTemp.X + vTemp.Length))
                    XOverlaps = true;

                // Checking for Y overlaps
                if(v.Y >= vTemp.Y && v.Y <= (vTemp.Y + vTemp.HandlingTime))
                    YOverlaps = true;   

                // Breaking the loop
                if(XOverlaps && YOverlaps)
                    return false;
            }
        }
        return true;
    }


}

Vessel.java

package geneticalgorithm;

import java.util.Random;

public class Vessel {

    Float X,Y,BA;
    Byte Z;

    Float ETA;
    Float Length;
    Float LowestCostBerthing;
    Float HandlingTime;
    Float SailingTime;
    Float DistancePenaltyCost;
    Float TimePenaltyCost;
    Float BunkeringDelayCost;
    Byte BunkeringNeeded;
    Float BunkeringProcessing;  

    void generateParams() {
        Random rand = new Random();
        Float MinBerth = 0.0f, MaxBerth = LowestCostBerthing + Length;

        if(LowestCostBerthing - Length > 0)
            MinBerth = LowestCostBerthing - Length;

        if(MaxBerth > DataLoader.Quay)
            MaxBerth = DataLoader.Quay - Length;

        X = rand.nextFloat() * (MaxBerth - MinBerth) + MinBerth;
        Y = rand.nextFloat() * ((ETA+24) - (ETA+1)) + (ETA+1);

        if(BunkeringNeeded==1)
        {
            Z = (byte) Math.round(Math.random()); // 0 OR 1
            if(Z!=0) {
                BA = Y;
            } else if(Y - ETA < BunkeringProcessing) {
                BA = SailingTime;
            } else if(Y - ETA > BunkeringProcessing) {
                BA = ETA;
            }

        } else {
            Z = 0;
            BA = 0.0f;
        }
    }

}

有什么想法吗?谢谢!

【问题讨论】:

  • 您没有发布相关代码。请发布Solution 课程。但是根据行为,我会说您将健身存储在某个静态字段中。请遵守 Java 命名约定:字段(除非 static final)应始终以小写字母开头。
  • 解决方案使用静态字段。使其成为非静态的,每个解决方案都有自己的价值。
  • 这些 cmets 是有见地的猜测,但它们只是猜测。请将此支持到 MVCE stackoverflow.com/help/mcve 中——到目前为止,我们所拥有的还不完整或无法验证。在使其最小化时,您可能会发现问题。如果是这样,请保持礼貌,在此处提供 MVCE 和答案。
  • @PhoenixNoor 然后显示类定义。每次创建一个新的解决方案,然后调用 getFintness 更改。尝试做Solution a = new Soluction(vessels); System.out.println(a.getFitness()); Solution b = new Solution(vessels); System.out.println(a.getFitness() + ", " + b.getFitness()); 所以要么你有静态字段或者你保持对“容器”的引用并修改它。
  • 您正在修改您的船只。您的每个解决方案都引用相同的容器。然后你修改它们,所以每个解决方案都会反映这些变化。

标签: java oop arraylist


【解决方案1】:

在这里尝试使用遗传算法,但我无法弄清楚为什么同一个数组在几行之后返回不同的结果。

每个Solution 都在Vessel 对象列表中传递。此列表用于在每个 Solution 内创建另一个 vessels 列表,但重要的是要注意新列表仍包含 原始 Vessel 对象。

this.vessels = new ArrayList<>(vessels);

此代码不会创建新的Vessel 对象——它只是创建相同对象的新列表。这意味着即使您创建了许多不同的Solutions,但每个Vessels 都在处理相同。每当调用generateSolution() 时,它都会在同一个共享Vessels 上调用vessel.generateParams() 并更新先前Solution 对象中的值。这就是为什么最后一个Solution 生成的值是你返回列表时打印出来的值。

我不确定其意图是什么,但如果您对此感到惊讶,那么您可能希望将 new Vessel 对象添加到您的每个解决方案中,而不是重复使用相同的列表。

编辑:

在查看您的 Vessel 对象时,您会发现一些确实是常量的字段:

Float Length;
Float LowestCostBerthing;
Float HandlingTime;

还有一些其他的计算字段:

Float X,Y,BA;
Byte Z;

您确实应该考虑将有关 Vessel 的常量与您的解决方案生成的计算分开。另外,顺便说一句,字段应该以小写字母开头,是原语(floatbyte)而不是最有可能的对象,如果它们是不可变的,则应该是 final

一个想法是有一个 VesselCalculation 对象来保存计算并且还有一个 Vessel 字段:

public class VesselCalculation {
    private final Vessel vessel;
    private float x, y, ba;
    private byte z;

那么Solution 将生成VesselCalculation 的列表,该列表将被修改。 Vessel 对象将与不同的人共享 VesselCalculation 对象,但将保持不可变。

private final List<VesselCalculation> vesselCalculations = new ArrayList<>();
public Solution(ArrayList<Vessel> vessels) {
    for (Vessel vessel : vessels) {
         vesselCalculations.add(new VesselCalculation(vessel));
    }
    generateSolution();
}

generateSolution() 然后将只更改此特定 Solution 本地的 VesselCalculation 对象。

【讨论】:

  • 好地方,Vessels 本身也在修改中。我想我已经习惯了修改这样的东西,我略读代码假设它们不会。
  • 是的,可变性是如此重要@slim,当有人有这样的模式时,我们会被绊倒。
  • 目的是有独立的不相关的副本,我们如何实现呢?感谢您的所有帮助。
【解决方案2】:

getFitness() 似乎只使用从vessels 提取的值。

由于每个Solution 都包含相同的vessels 实例,因此每个解决方案都将为getFitness() 返回相同的响应,给定vessels 的特定状态。

Solution 的构造函数调用generateSolution(),它修改了vessels 的内容。这就是每次您创建新的Solution 时您的getFitness() 结果都会发生变化的原因。

我不知道您所需算法的详细信息,但似乎每个Solution 都应该有自己的列表副本。它是否应该在其构造函数中创建该副本,或者是否调用构造函数的任何内容都负责创建该副本,这取决于您。

因为Vessel 对象本身是可变的(并且使用了可变性),您还需要复制它们,而不是制作另一个包含对相同对象的引用的列表。

在这种情况下,使用不可变集合不可变对象可以省去很多麻烦。

【讨论】:

    【解决方案3】:

    如果您遇到同样的问题,我可以通过使用 this solution 解决此问题。这个想法是循环遍历数组并在克隆它们时一个一个地复制每个元素。在 ArrayList 上调用 clone() 函数没有任何效果,但会在保持相同引用的同时克隆 ArrayList。

    您将不得不实现可克隆接口并在手动克隆每个字段时覆盖克隆方法。

    【讨论】:

    • 查看我的答案@PheonixNoor。更好的解决方案是将常量与计算字段分开。
    猜你喜欢
    • 2018-12-23
    • 1970-01-01
    • 2013-09-26
    • 1970-01-01
    • 2020-02-29
    • 2013-10-10
    • 1970-01-01
    • 1970-01-01
    • 2012-10-13
    相关资源
    最近更新 更多