首页 > 解决方案 > 为什么 indexOf() 和 contains() 在 java 中不能像我预期的那样工作?

问题描述

// 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));

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

Animal Farm has been bought
Animal Farm is already bought

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

Animal Farm has been bought
Animal Farm has been bought

使用 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)

这是有效的原始方法

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;
}

这是我不知道为什么它不起作用的方法,我打算如何使这个方法起作用,如果变量searchItem返回 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;
  }
}

这也没有按我的预期工作。我希望这种方法起作用的方式是,当我找到我正在寻找的元素的索引位置,然后将它与列表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;
}

商店类创建任意商店类

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);        
}

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

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;
  }  
}
    

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

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;
  }
}

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

标签: javaarraysobjectcontainsindexof

解决方案


解决方案:

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

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

从以下文档contains

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

覆盖规则equals

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

  • reflexive : 一个对象必须等于它自己
  • 对称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 自动生成equalshashCode并且只使用final这两种方法中的字段来确定它们的返回值。

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


推荐阅读