为什么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;
}
这是我不知道为什么它不工作,我打算如何这个方法来工作,如果变量的方法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;
}
}
这也不能按我的意图工作。我打算使用此方法的方式是,当我找到要查找的元素的索引位置,然后将其与列表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;
}
}
显然,我知道我对它应该如何工作的假设是错误的,问题在于那些内置方法以及我希望它们如何工作,但事实并非如此。
回答
解决方案:
您必须实现equals和hashCodein 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 自动生成这两种方法。