contains with ObservableList () returns 始终为 false

contains with ObservableList () returns always false

我在我的 JavaFX 代码中使用 ObservableList,我试图测试该列表中是否存在一个对象,但它总是 returns 错误,即使对于已经存在于其中的对象也是如此。 这是我的代码:

private ObservableList<OMission> UserMission = FXCollections.observableArrayList();    

OMission OM1 = new OMission(1,"user_firstname","user_lastname");


UserMission.add(OM1);



if(UserMission.contains(new OMission(1,"user_firstname","user_lastname")){

System.out.println("true");}

else {

System.out.println("false");

}

我期待得到“真”,但我总是得到假

发生了什么事?

您可能忽略了为 equalshashCode 实施关键的 Object 覆盖。

此问题不是 OpenJFX class ObservableList 所特有的。执行比较的任何集合(列表、集合、映射等)都将取决于您是否为至少 equals 和可能 hashCode 编写适当的覆盖。 (提示:总是覆盖两者或两者都不覆盖,永远不要单独覆盖一个。)

下面是使用您的代码的修改版本的示例代码。顺便说一下,你少了一个 right-paren。此外,如果您遵循 Java 命名约定,生活会更轻松。

为简洁起见,我们使用 Java 16+ 中的新 records 功能。记录是编写 class 的一种更简洁的方式,其主要目的是透明且不可变地传递数据。您只需要声明每个成员字段的类型和名称。编译器隐式创建构造函数、getter、equals & hashCodetoString。最后三种方法默认检查每个成员字段的值。

为简单起见,我们在本地声明了 record。您也可以声明它是嵌套的还是单独的。

package work.basil.example;

import javafx.collections.*;

public class App {
    public static void main ( String[] args ) {
        System.out.println ( "Hello World!" );
        App app = new App ();
        app.demo ();
    }

    private void demo () {
        record Person( int id , String firstName , String lastName ) { }

        ObservableList < Person > UserMission = FXCollections.observableArrayList ();
        Person p1 = new Person ( 1 , "Alice" , "Anderson" );
        UserMission.add ( p1 );
        if ( UserMission.contains ( new Person ( 1, "Alice" , "Anderson" ) ) ) {
            System.out.println ( "true" );
        } else {
            System.out.println ( "false" );
        }
    }
}

当运行.

Hello World!

true

如果使用 Java 的早期版本,或者如果记录不适合您的情况,请编写类似于以下内容的 class。注意方法 equals & hashCode.

package work.basil.example;

import java.util.Objects;

public final class Person {
    private final int id;
    private final String firstName;
    private final String lastName;

    public Person ( int id , String firstName , String lastName ) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public int id () { return id; }

    public String firstName () { return firstName; }

    public String lastName () { return lastName; }

    @Override
    public boolean equals ( Object obj ) {
        if ( obj == this ) return true;
        if ( obj == null || obj.getClass () != this.getClass () ) return false;
        var that = ( Person ) obj;
        return this.id == that.id &&
                Objects.equals ( this.firstName , that.firstName ) &&
                Objects.equals ( this.lastName , that.lastName );
    }

    @Override
    public int hashCode () {
        return Objects.hash ( id , firstName , lastName );
    }

    @Override
    public String toString () {
        return "Person[" +
                "id=" + id + ", " +
                "firstName=" + firstName + ", " +
                "lastName=" + lastName + ']';
    }
}

覆盖 equals/hashCode 的问题已经讨论过很多次了。 Search to learn more.

Basil 在我撰写本文时同时更新了他的答案以包含一些附加信息。因此,此答案中有一些信息是重复的。请原谅这里的任何重复。我暂时保留这个答案。


为了补充 Basil 的回答,一些不同的解决方案是:

  1. 编写 equals 和 hashcode 的自定义实现。

对于 OMission,实施 equals(Object obj)(最好也实施 hashCode())。

您最终会得到类似于 Basil 示例答案中的代码。

您可以自己从头开始编写这些方法,但是,我不推荐这样做。手动编写这些函数时很容易犯不必要的愚蠢错误。

  1. 使用 IDE 自动生成 equals 和 hashcode 的实现。

大多数 IDE 都有一个菜单项或快捷方式来自动生成这些方法(请参阅 how to do this in Idea)。这就是我通常生成这些函数的方式。如果我向 class 添加或删除一个字段,那么我会删除以前自动生成的函数并自动生成新的。

  1. 使用第 3 方库生成等号和哈希码。

第三方库,例如Lombok, can be used to autogenerate the methods. Lombok will do this via annotations,例如只需将注释 @EqualsAndHashcode 添加到您的 class 定义中即可。

  1. 将 OMission 设为记录而不是 class。

如果可以将数据表示为不可变 record 而不是 class 是合适的,那么这是推荐的方法。它不适用于所有数据类型。

默认情况下,记录实现适当的 equals 和 hashcode 方法。

record OMission(
    int missionNum, 
    String firstname, 
    String lastname
) {}