【问题标题】:Compare object with deserialised object将对象与反序列化对象进行比较
【发布时间】:2012-04-17 15:18:18
【问题描述】:

我有一个带有 JList 的 Swing 应用程序,它与自定义列表模型一起使用。

模型有一个 ArrayList 来存储使用的对象。情况如下:

关闭应用程序后,listmodel 中的所有对象都被序列化(默认情况下,该类只实现Serializable),并通过ObjectOutputStream 写入文件。当应用程序启动时,从文件中读取所有对象并再次存储在我的自定义ListModel 中。

我正在尝试添加一项功能,让用户也可以从他指定的文件中导入对象。另一个类中的静态方法从文件中读取所有对象,并在ArrayList 中返回它们。然后我在MainForm 类中使用for-each 循环将返回的ArrayList 中的每个对象存储在ListModel 中。在循环中,我想检查ListModel 是否已经包含某个对象,但这不起作用

在代码中,我正在执行以下操作:

for(MyObject o: readObjects) {
    if(!myListModel.contains(o)) //listmodel just calls contains() on its ArrayList
        myListModel.addElement(o);
}

但是,即使对象已经在 ArrayList 中(我一直从同一个文件中导入对象),它们仍然会被添加。

问题是,为什么反序列化后对象不再相等,有没有办法比较它们?

【问题讨论】:

  • 您是否在您的对象类中覆盖equals() 方法?
  • @MarioDeSchaepmeester 如果您不覆盖 equals,则 contains 将测试引用相等性(Object 类中 equals 的默认行为)。即使不使用序列化,如果您创建 2 个具有相同字段的对象,上面的代码也不会达到您的预期(例如:list.add(new MyObject());list.contains(new MyObject()) 将返回 false)。
  • 是的,当反序列化时,您的对象将被重新创建并具有新的引用。如果您不覆盖equals() 方法,则它是刚刚检查的引用。 (我认为)
  • @assylias,你们中的任何一个人能否解释一下为了在覆盖方法时成功比较 2 个对象应该做什么?有什么最佳做法吗?我已经看到人们在他们的课程中放置了序列号或其他东西,这是我应该使用的吗?我以前没用过这种东西……
  • 我认为 serialUID 与此无关。如果您使用的是 Eclipse 之类的 IDE,您可以要求它自动覆盖 equals()。基本上,您必须选择对象的哪些字段用于比较两个对象。

标签: java object serialization equals


【解决方案1】:

这是一个简单的java对象

public class MyObject {

private String firstname;
private String lastname;
private String email;

public String getFirstname() {
    return firstname;
}
public void setFirstname(String firstname) {
    this.firstname = firstname;
}
public String getLastname() {
    return lastname;
}
public void setLastname(String lastname) {
    this.lastname = lastname;
}
public String getEmail() {
    return email;
}
public void setEmail(String email) {
    this.email = email;
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
            + ((firstname == null) ? 0 : firstname.hashCode());
    result = prime * result
            + ((lastname == null) ? 0 : lastname.hashCode());
    return result;
}
@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    MyObject other = (MyObject) obj;
    if (firstname == null) {
        if (other.firstname != null)
            return false;
    } else if (!firstname.equals(other.firstname))
        return false;
    if (lastname == null) {
        if (other.lastname != null)
            return false;
    } else if (!lastname.equals(other.lastname))
        return false;
    return true;
}
}

(eclipse自动生成hashcode和equals)

如果您查看 equals() 方法,您将看到在字段 firstname 和 lastname 上比较了 2 个对象,并且仅比较了这 2 个。这意味着如果您将这种类型的对象插入到列表中,您将能够使用contains(Object o),如果一个对象包含相同的名字和姓氏,你会找到它。

【讨论】:

  • 谢谢,很清楚。但是,为每个对象设置一个随机生成的 ID 为long 不是更好吗?因为我的对象也有名字和姓氏,但这不是因为两个人碰巧有相同的名字和姓氏,他们是同一个人......当对象是另一个对象但名字和姓氏相同时它会起作用吗名字?
  • @MarioDeSchaepmeester 您需要编写一个 equals 方法来为您提供所需的行为 - 这是一个设计决策。如果 2 个不同的人可以有相同的名字,因此 equals 应该返回 false,您需要向您的类添加一个唯一标识符,equals 方法将使用该标识符而不是名字来测试是否相等。
【解决方案2】:

您需要覆盖对象的 equals 和 hashcode 方法(SO 上提供了许多示例,例如 here

完成此操作后,List 的contains 方法将按照您的预期运行。您的问题似乎与序列化无关。也就是说,如下代码:

List<MyObject> list = new ArrayList<MyObject>();
list.add(new MyObject());
System.out.println(list.contains(new MyObject()));

如果您不覆盖 equals & hashcode,则将打印 false,而如果您这样做,它可能会打印 true(取决于您的 equals 方法是否认为这 2 个 new MyObject() 相等)。你的 IDE 应该有办法为你自动生成代码。

一旦您对 contains 方法在您的对象上按预期工作感到满意,序列化/反序列化应该按预期工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 2022-09-30
    • 1970-01-01
    • 2015-07-17
    相关资源
    最近更新 更多