Как определить наличие объекта в ObservableList?

в этой программе я пытался реализовать добавление элементов в таблицу, но столкнулся с тем, что не могу реализовать ограничение на добавление копий, пытался использовать метод contains, но безуспешно.

package com.example.demo;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;

import java.util.Objects;

public class Controller {

    private static ObservableList<Ticket> ticketsData = FXCollections.observableArrayList();

    @FXML
    private Button addButton;
    @FXML
    private Button delButton;
    @FXML
    private RadioButton bookingStatusRadioButton;
    @FXML
    private TableColumn<Ticket, String> bookingStatusTableColumn;
    @FXML
    private TableColumn<Ticket, String> flightCipherTableColumn;
    @FXML
    private TableColumn<Ticket, String> placeNumberTableColumn;
    @FXML
    private TextField flightCipherTextField;
    @FXML
    private Label infoLabel;
    @FXML
    private MenuItem saveAsMenuItem;
    @FXML
    private MenuItem saveMenuItem;
    @FXML
    private MenuItem lookMenuItem;
    @FXML
    private MenuItem openMenuItem;
    @FXML
    private MenuItem closeMenuItem;
    @FXML
    private MenuItem addMenuItem;
    @FXML
    private MenuItem aboutMenuItem;
    @FXML
    private TextField placeNumberTextField;
    @FXML
    private TableView<Ticket> tableTickets;

    @FXML
    void initialize() {
        closeMenuItem.setOnAction(actionEvent -> System.exit(0));
        aboutMenuItem.setOnAction(actionEvent -> showAlertWithDefaultHeaderText());
        initData();
        // устанавливаем тип и значение которое должно хранится в столбце
        flightCipherTableColumn.setCellValueFactory(new PropertyValueFactory<Ticket, String>("flightCipher"));
        placeNumberTableColumn.setCellValueFactory(new PropertyValueFactory<Ticket, String>("placeNumber"));
        bookingStatusTableColumn.setCellValueFactory(new PropertyValueFactory<Ticket, String>("bookingStatus"));

        // заполняем таблицу данными
        tableTickets.setItems(ticketsData);
    }

    //ввод данных в таблицу из графического интерфейса
    private void initData() {
        addButton.setOnAction(actionEvent -> {

            if (Objects.equals(flightCipherTextField.getText(), "")) {
                infoLabel.setText("Необходимые поля пусты");
            } else if (Objects.equals(placeNumberTextField.getText(), "")) {
                infoLabel.setText("Необходимые поля пусты");
            } else if (ticketsData.contains(new Ticket(flightCipherTextField.getText(), placeNumberTextField.getText(), bookingStatusRadioButton.isSelected()))) {
                infoLabel.setText("Данный билет уже имеется в базе");
            } else {
                infoLabel.setText("Данные внесены");
                ticketsData.add(new Ticket(flightCipherTextField.getText(), placeNumberTextField.getText(), bookingStatusRadioButton.isSelected()));
            }
        });
    }
}
package com.example.demo;

public class Ticket {
    private String flightCipher;
    private String placeNumber;
    private String bookingStatus;

    public Ticket(String flightCipher, String placeNumber, boolean bookingStatus){
        this.flightCipher = flightCipher;
        this.placeNumber = placeNumber;
        this.bookingStatus = String.valueOf(bookingStatus);
    }


    public String getFlightCipher() {
        return flightCipher;
    }

    public void setFlightCipher(String flightCipher) {
        this.flightCipher = flightCipher;
    }

    public String getPlaceNumber() {
        return placeNumber;
    }

    public void setPlaceNumber(String placeNumber) {
        this.placeNumber = placeNumber;
    }

    public String getBookingStatus() {
        return bookingStatus;
    }

    public void setBookingStatus(String bookingStatus) {
        this.bookingStatus = bookingStatus;
    }

}
          package com.example.demo;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class Main extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 640, 440);
        stage.setTitle("База данных авиабилетов");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

Ответы (1 шт):

Автор решения: insolor

Метод .contains() проверяет наличие объекта в List через сравнение каждого объекта в коллекции с нужным объектом через метод .equals(). По умолчанию метод .equals() (унаследованный от класса Object) проверяет только равенство ссылок, но не равенство содержимого, поэтому .equals() возвращает true только если объект будет сравниваться с самим собой.

Чтобы сравнение шло по содержимому полей объекта, нужно переопределить метод .equals(), в нем делать сравнение двух объектов по их полям.

Если пишите в Intellij IDEA, можно сгенерировать этот метод автоматически: внутри класса нажимаете Alt+Enter, в появившемся меню "Generate" выбираете "equals() and hashCode()", дальше уточняете параметры (стиль, сравниваемые поля, и т.д.)

hashCode для вашей задачи (проверки наличия внутри ObservableList или List в целом) не нужен, но понадобится, если вы захотите хранить и проверять наличие вашего объекта внутри Set или использовать как ключ в Map.

Я приведу немного модифицированный вариант .equals() с комментариями:

@Override
public boolean equals(Object other) {
    if (this == other) return true;  // Если один и тот же объект, значит объекты равны
    if (other == null || getClass() != other.getClass()) return false;  // Если классы не совпадают, значит объекты разные
    Ticket ticket = (Ticket) other;

    // Если значения полей совпадают, значит объекты равны
    return flightCipher.equals(ticket.flightCipher)
            && placeNumber.equals(ticket.placeNumber)
            && bookingStatus.equals(ticket.bookingStatus);
}

Для проверки можно использовать такой код:

ObservableList<Ticket> ticketsData = FXCollections.observableArrayList();

ticketsData.addAll(
        new Ticket("123", "place 111", true),
        new Ticket("456", "place 123", true),
        new Ticket("789", "place 652", false)
);

// Выведет "contains"
if (ticketsData.contains(new Ticket("456", "place 123", true))) {
    System.out.println("contains");
} else {
    System.out.println("doesn't contain");
}

// Выведет "doesn't contain"
if (ticketsData.contains(new Ticket("erewr", "place 1234", false))) {
    System.out.println("contains");
} else {
    System.out.println("doesn't contain");
}
→ Ссылка