为什么 indexOf() 和 contains() 不能像我在 Java 中预期的那样工作?

moc*_*ird 0 java arrays contains object indexof

// Objects for the bookstore I created

BookStore bookStore = new BookStore("Subu's Book Store","Perambur");
bookStore.addItem(new Item("Animal Farm",20));
bookStore.addItem(new Item("Animal Farm",20));
Run Code Online (Sandbox Code Playgroud)

如果我使用原始工作方法,则输出:

Animal Farm has been bought
Animal Farm is already bought
Run Code Online (Sandbox Code Playgroud)

使用 contains() 不起作用的方法的输出

Animal Farm has been bought
Animal Farm has been bought
Run Code Online (Sandbox Code Playgroud)

使用 indexof() 不起作用的方法的输出

-1
Animal Farm has been bought
-1
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 1
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:372)
at java.base/java.util.ArrayList.get(ArrayList.java:459)
at com.company.subusproject.BookStore.searchItem(BookStore.java:41)
at com.company.subusproject.BookStore.addItem(BookStore.java:16)
at com.company.subusproject.Main.main(Main.java:11)
Run Code Online (Sandbox Code Playgroud)

这是有效的原始方法

public boolean searchItem(Item item) {
  for (int i = 0; i < this.myStocks.size(); i++) {
    Item foundItem = this.myStocks.get(i);
    if (foundItem.getName().equals((item.getName()))) {
      return true;
    }
  }
  return false;
}
Run Code Online (Sandbox Code Playgroud)

这是我不知道为什么它不工作,我打算如何这个方法来工作,如果变量的方法searchingItem返回true,则该方法searchItem将返回如果没有返回false

public boolean searchItem(Item item) {
  // this method returns the boolean value true if found, if not returns false   
  boolean searchingItem = this.myStocks.contains(item);
  if(searchingItem){
    return true;
  } else {
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)

这也不能按我的意图工作。我打算使用此方法的方式是,当我找到要查找的元素的索引位置,然后将其与列表myStocks中可用的元素进行比较时,在这种情况下也不是这种情况。

public boolean searchItem(Item item) {
  // this method returns the index number of the founded element, if not returns -1
  int itemPosition = this.myStocks.indexOf(item); // this line always returns -1 in this scenario
  for(int i=0; i<this.myStocks.size(); i++){
    if(myStocks.get(itemPosition).getName().equals(myStocks.get(i).getName())){
      return true;
    }
  }
  return false;
}
Run Code Online (Sandbox Code Playgroud)

商店类以创建任何商店类

public abstract class Store {
  private String name;
  private String address;
    
  public Store(String name, String address) {
    this.name = name;
    this.address = address;
  }
    
  public abstract boolean addItem(Item item);
    
  public abstract boolean removeItem(Item item);
    
  public abstract boolean searchItem(Item item);        
}
Run Code Online (Sandbox Code Playgroud)

基本项目类添加任何类型的项目

public class Item {
    
  private String name;
  private int price;
    
  public Item(String name, int price){
    this.name = name;
    this.price = price;
  }
    
  public String getName(){
    return this.name;
  }
    
  public int getPrice(){
    return this.price;
  }  
}
    
Run Code Online (Sandbox Code Playgroud)

具有添加和搜索方法的实际书店类

public class BookStore extends Store {
        
  private List<Item> myStocks;
        
  public BookStore(String name, String address) {
    super(name, address);
    this.myStocks = new ArrayList<>();
  }
        
  public boolean addItem(Item item) {
    if (!searchItem(item)) {
      this.myStocks.add(item);
      System.out.println(item.getName() + " has been bought");
      return true;
    } else {
      System.out.println(item.getName() + " is already bought");
      return false;
    }
  }
        
  public boolean searchItem(Item item) {
    for (int i = 0; i < this.myStocks.size(); i++) {
      Item foundItem = this.myStocks.get(i);                
      if(foundItem.getName().equals((this.myStocks.get(i).getName()))) {
        return true;
      }
    }
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)

显然,我知道我对它应该如何工作的假设是错误的,问题在于那些内置方法以及我希望它们如何工作,但事实并非如此。

Mar*_*ger 7

解决方案:

您必须实现equalshashCodein Item,因为contains依赖于equals.

如果equals默认情况下不覆盖Java,则使用引用相等

从文档contains

返回true此列表是否包含指定的元素。更正式地说,true当且仅当此列表包含至少一个元素e使得(o==null ? e==null : o.equals(e)).

覆盖规则equals

部分取自此处:Java SE 定义了equals必须履行的合同。该equals方法必须是:

  • 自反:对象必须等于自身
  • 对称x.equals(y)必须返回相同的结果y.equals(x)
  • 传递性:如果x.equals(y)y.equals(z)随后也x.equals(z)
  • 一致equals仅当包含在其中的属性发生更改时,的值才应equals更改(不允许随机性)

为什么还要覆盖hashCode

也部分取自这里。Java SE 还为该hashCode方法定义了一个协定。彻底看看它显示了如何密切相关hashCode,并equals有。合同中的所有三个标准在hashCode某些方面都提到了equals方法:

  • 内部一致性: 的值hashCode可能仅在更改的属性时equals更改
  • 相等一致性:彼此相等的对象必须返回相同的哈希码
  • 碰撞:不相等的对象可能具有相同的哈希码(即,仅仅因为两个对象具有相同的哈希码并不意味着它们相等)

我建议使用 IDE 自动生成equals并且hashCode只使用final两种方法中的字段来确定它们的返回值。

只有遵循这些规则,我们才能期望标准库(或任何其他数据结构)中的数据结构正常工作。

  • @Basya 因为`equals` 的文档要求在覆盖`equals` 时也必须实现`hashCode`。方法在任何时候都可以依赖于这个事实。因此,即使 `contains` 今天可能不使用 `hashCode`,明天也可能会使用它。只需使用您的 IDE 自动生成这两种方法。 (2认同)