【问题标题】:Implementing an equals() method to compare contents of two 'bag' objects实现一个 equals() 方法来比较两个“bag”对象的内容
【发布时间】:2016-05-25 03:19:09
【问题描述】:

我正在完成一项学校作业。目标是练习 GUI、clone() 方法以及使用/修改现有代码。 我正在尝试以教师希望的方式编写 equals 方法——通过使用对象的克隆,从包中删除项目(根据删除成功或失败返回布尔值)。 p>

包以数组表示,在{1,2,3}和{3,2,1}等情况下应返回true,即顺序无关紧要,只有每个数字的个数出现在数组。

问题来了

它在大多数情况下都有效,但是在袋子包含以下数字的情况下存在错误:{1,1,2} 和 {1,2,2} 以及其他类似的迭代。它返回的是 true 而不是 false。

我相信这与我们应该使用的 remove() 方法有关。如果我理解正确,它应该将值放在数组的“末尾”并减少 manyItems 计数器(这是数组中项目数的变量,因为 array.length 在构造函数中默认为 10 .)

代码大部分是由另一个人编写的。我们必须导入现有文件并编写新方法来完成我们被赋予的任务。我已经完成了所有的 GUI 部分,所以我不会包含那个类,只包含 IntArrayBag 类中使用的方法。

第二双眼睛会很有帮助。谢谢。

public class IntArrayBag implements Cloneable
{
// Invariant of the IntArrayBag class:
//   1. The number of elements in the bag is in the instance variable 
//      manyItems, which is no more than data.length.
//   2. For an empty bag, we do not care what is stored in any of data;
//      for a non-empty bag, the elements in the bag are stored in data[0]
//      through data[manyItems-1], and we don�t care what�s in the
//      rest of data.
private int[ ] data;
private int manyItems;

public IntArrayBag( )
{
  final int INITIAL_CAPACITY = 10;
  manyItems = 0;
  data = new int[INITIAL_CAPACITY];
}

public IntArrayBag clone( )
{  // Clone an IntArrayBag object.
  IntArrayBag answer;

  try
  {
     answer = (IntArrayBag) super.clone( );
  }
  catch (CloneNotSupportedException e)
  {  // This exception should not occur. But if it does, it would probably
     // indicate a programming error that made super.clone unavailable.
     // The most common error would be forgetting the "Implements Cloneable"
     // clause at the start of this class.
     throw new RuntimeException
     ("This class does not implement Cloneable");
  }

  answer.data = data.clone( );

  return answer;
}

public int size( )
{
  return manyItems;
}

public boolean remove(int target)
{
  int index; // The location of target in the data array.

  // First, set index to the location of target in the data array,
  // which could be as small as 0 or as large as manyItems-1; If target
  // is not in the array, then index will be set equal to manyItems;
  for (index = 0; (index < manyItems) && (target != data[index]); index++)
     // No work is needed in the body of this for-loop.
     ;

  if (index == manyItems)
     // The target was not found, so nothing is removed.
     return false;
  else
  {  // The target was found at data[index].
     // So reduce manyItems by 1 and copy the last element onto data[index].
     manyItems--;
     data[index] = data[manyItems];
     return true;
  }
}
//I added extra variables that are not needed to try to increase readability,
//as well as when i was trying to debug the code originally
public boolean equals(Object obj){

   if (obj instanceof IntArrayBag){

       IntArrayBag canidate = (IntArrayBag) obj; // i know this can be changed, this was required
       IntArrayBag canidateTest = (IntArrayBag) canidate.clone(); //this was created
       //as a clone because it was otherwise referring to the same memory address
       //this caused items to be removed from bags when testing for equality
       IntArrayBag test = (IntArrayBag) this.clone();

       //fast check to see if the two objects have the same number of items, 
       //if they dont will return false and skip the item by item checking
       if (test.size() != canidateTest.size())
           return false;

       //the loop will go through every element in the test bag it will 
       //then remove the value that is present at the first index of the test bag
       for (int i = 0; (i < (test.size()) || i < (canidateTest.size())); i++){
           int check = test.data[i];
           //remove() returns a boolean so if the value is not present in each bag
           //then the conditional will be met and the method will return false
           boolean test1 = test.remove(check);
           boolean test2 = canidateTest.remove(check);
           if (test1 != test2)
               return false;

       }//end for loop

       // if the loop goes through every element
       //and finds every value was true it will return true
       return true;  
   }//end if 
   else
       return false;
}//end equals
}

【问题讨论】:

  • 也许我遗漏了一些东西,但我没有在您的帖子中看到具体问题。请明确说明您要问的是什么。
  • 为什么要克隆equals中的对象?
  • 编辑应该反映您的 cmets。
  • 感谢大家尝试提出其他解决问题的方法。非常感谢。不幸的是,当我们第一次得到作业时,我问老师是否可以用另一种方式解决问题。她坚持要我们克隆这些物体。它的编写方式在大多数情况下都有效。我正在调试,并且 remove() 方法出现了问题,有时它无法正常运行(在一种情况下,它将 1 变成了 2)。这个方法不是我写的。我尝试修改该方法,但无济于事,我无法使其按预期工作。
  • 坚持使用clone(),老师有解释吗?它仍然有一些防御者,但 clone()Cloneablegenerally considered broken,你很少会遇到他们的实际用例。

标签: java arrays object clone equals


【解决方案1】:

我看不到全局,因为我之前没有用 Java 编写过 GUI,但是,就比较 2 个 int[] 数组而言,我会在比较之前对数组进行排序。这将允许您消除您所说的问题案例(如果可以进行排序),然后应用以下内容:

while(array_1[index]==array_2[index] && index<array_1.length)
{index++;}

并通过检查 index 的最终值找到循环在哪里中断

【讨论】:

  • 我想如果对克隆的数组进行排序,这可能会起作用(我们应该保持原始数组不变,以便在打印时可以看到顺序)。一种可能的解决方法。
【解决方案2】:

是否明确规定使用克隆?您可以通过覆盖此对象的hashCode() 轻松实现它。

您可以按如下方式覆盖此对象的hashCode()

@Override
public int hashCode() {
    final int prime = 5;
    int result = 1;
    /* Sort Array */
    Arrays.sort(this.data);
    /* Calculate Hash */
    for(int d : this.data) {
        result = prime * result + d;
    }
    /* Return Result */
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || this.getClass() != obj.getClass()){
            return false;
    }
    return false;
}

如果您想继续使用您的 equals 实现来比较 testCandidateTest,那么您也可以计算唯一的哈希值并根据结果做出决定。

这里是sn-p的代码:

/* Assuming that you have put size comparison logic on top 
   and the two objects are of same size */

final int prime = 31;
int testResult = 1;
int candidateTestResult = 1;
for(int i = 0; i < test.size(); i++) {
    testResult = prime * testResult + test.data[i];
    candidateTestResult = prime * candidateTestResult + candidateTest.data[i];
}
/* Return Result */
return testResult == candidateTestResult;

【讨论】:

  • 不幸的是,我们应该使用克隆。
  • @Brain O。不是比较单个元素,而是在克隆方法中计算两个对象的哈希值。请查看更新后的解决方案。
  • 您不能通过测试哈希码来测试相等性。由于哈希码比数组短,这意味着在某些情况下,数组具有不同的元素但映射到相同的哈希码。对于应该为“假”的情况,您将收到true。这个答案是完全错误的。
  • @ajb 感谢您的回复。我了解计算出的hashCode() 的限制为INT_MAX。但我不明白不同的元素如何映射到相同的哈希码。我在我的许多 POJO 课程中都这样做,但从未遇到过问题。可以举个例子吗?
  • 如果可以的话,我会使用你的解决方案。一些教练可能会搞砸。他们想按照自己的方式行事。我认为散列已经有几章了,所以它们是禁区。不过,我很感激你花时间帮助我。
【解决方案3】:

我认为问题出在这一行:

for (int i = 0; (i < (test.size()) || i < (canidateTest.size())); i++){

这里的问题是testcanidateTest 是您创建的克隆,并且您正在从这些包中删除元素。并且任何时候你从包中移除一个元素,size 都会减少(因为你减少了manyItems,而size() 返回manyItems)。这意味着您只会通过一半的阵列。假设原来的大小是4。那么,第一次通过循环,i==0test.size()==4;第二次,i==0test.size()==3;第三次,i==2test.size()==2,然后你退出循环。所以你不看所有 4 个元素——你只看 2 个。

您需要决定:您是要遍历原始数组的元素还是克隆的元素?如果你浏览了克隆的元素,你实际上永远不需要增加i。您可以随时查看test.data[0],因为一旦您查看它,您就将其删除,所以您知道test.data[0] 将被其他东西替换。事实上,你根本不需要i。只需循环直到袋子大小为 0,或者直到您确定袋子不相等。另一方面,如果您遍历this.data 的元素(即查看this.data[i] 或只是data[i]),则确保i 一直到this.size()

(还有一点:正确的拼写是“candidate”。)

【讨论】:

  • 非常感谢。我更改了循环控制,使其与您所说的删除无关。我做了一些测试,它是成功的。不过,我对我的错误的简单性感到尴尬。再次,非常感谢。
  • 这是一个常见的错误。当您在从数组或列表中删除元素(或插入/附加元素)的同时循环遍历数组或列表时,您必须非常小心。
  • 我也尝试了省略增量的方法,这也有效。感谢您的帮助。
【解决方案4】:

也许你应该试试 SET 接口 详细查看:http://www.tutorialspoint.com/java/java_set_interface.htm 集合对象不能包含重复的元素,因此它比构建自己的类更适合您的作业。

例如:[1,1,2] 和 [1,2,2] 你可以用它来测试它们是否相等

arr1 = {1,1,2}
arr2 = {1,2,2}
Set<Integer> set = new HashSet<Integer>();
for(int i : arr1){//build set of arr1
    if(set.contains(i)==false){
        set.add(i)
    }
}
for(int i:arr2){
    if(set.contains(i)==false){
        System.out.println('not equal');
        break;
    }
}

希望这有帮助。

【讨论】:

  • 虽然你是对的,集合不能包含重复项,但这并不能回答这个问题,因为需要使用克隆
  • 看来这个答案是可以使用的,不幸的是我们需要通过克隆来做到这一点。感谢您抽出宝贵时间回答
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
  • 2018-04-28
  • 1970-01-01
  • 2017-07-28
  • 2012-01-15
  • 2013-09-06
相关资源
最近更新 更多