从ArrayList的contains说开来
一、 问题的发现
最近一个小弟在使用ArrayList的contais方法的时候出现了意想不到的结果,遂跑过来问我是怎么回事。他的代码大致是这样的:有一个自定义的User类,有一个ArrayList型的User列表,调用该列表的contains方法判断是否包含某User。具体代码为:
import java.util.ArrayList;
import java.util.List;
public class ArrayListContainsTest {
public static void main(String[] args){
List users = new ArrayList();
User user1 = new User("001");
User user2 = new User("002");
users.add(user1);
users.add(user2);
System.out.println(users.contains(new User("001")));
}
}
class User{
private String userID;
private String userName;
public User(String userID){
this.userID = userID;
}
public String getUserID(){
return this.userID;
}
public void setUserID(String userID){
this.userID = userID;
}
public String getUserName(){
return this.userName;
}
public void setUserName(String userName){
this.userName = userName;
}
public boolean equals(User user){
if(user == null) return false;
if(this == user) return true;
if(userID == null){
if(user.userID != null)
return false;
} else if(!userID.equals(user.userID))
return false;
return true;
}
}
意想中的结果应该为打印true,因为JDK Doument中对ArrayList.contains(Object o)的说明是这样的:
如果此列表中包含指定的元素,则返回 true。更确切地讲,当且仅当此列表包含至少一个满足 (o==null ? e==null : o.equals(e)) 的元素 e 时,则返回 true。
也就是说contains(Object o)方法的返回值分两种情况得出:
1. 如果参数o为null,并且ArrayList中存在null元素,则返回真;
2. 如果参数o非null,则结果根据o的equals方法遍历ArrayList确定,具体来说,就是判断ArrayList中是否contains元素o,就是判断ArrayList中是否存在这样的元素e使得o.equals(e)。
在上边的代码中,当调用user.contains(new User(“001”))时,参数并不为null,因此根据第二种情况,就是要users 中是否存在一个user满足(new User(“001”)).equals(user),从这个层面上讲,上面的代码确实应该返回true才对。但是仔细想来,里边却存在着问题。那么问题到底出在哪里呢?
我们首先在User类的equals方法中加入这样一行代码:
System.out.println("User.equals(User) has been called.");
这行代码的作用是在该方法被调用时有“User.equals(User) has been called.”输出。
再次运行测试代码,发现控制台依然只输出了“false”,而没有我们上面加入的语句的输出,这就说明了我们写的equals方法并没有被调用。看到这里,我们似乎发现了问题的所在。再回过头去看JDK Document中对contains方法的说明,遍历ArrayList时调用的是Object的equals方法,而User是Object的子类(Object类是所有类的父类),因此应该调用User的equals方法,而该equals方法应该是重写(override)Object的equals方法,Object的equal方法的定义为:
public boolean equals(Object obj)
而测试代码中的equals方法定义为:
public boolean equals(User user)
表面上看似乎确实重写了Object的equals方法,但实质上却不然。因为子类重写父类的方法必须保证方法的申明与父类完全相同,即参数的类型也应该相同,但测试方法中的参数却为User类型,而不是期望的Object 类型,因此测试方法中的equals方法只是User类中定义的一个新方法而已。
二、 问题修改
既然发现了问题,我们就从问题入手。在User类中override Object类的equals方法:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((userID == null) ? 0 : userID.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
System.out.println("User.equals(Object) has been called.");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final User other = (User) obj;
if (userID == null) {
if (other.userID != null)
return false;
} else if (!userID.equals(other.userID))
return false;
return true;
}
再次运行测试程序,有如下输出:
User.equals(Object) has been called.
true
终于达到了我们想要的效果。写到这里需要提醒的是,equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
三、 源码剖析
为了验证我们的推理过程,我们查看了ArrayList的contains方法的源码:
public boolean contains(Object elem) {
return indexOf(elem) >= 0;
}
ArrayList的index 方法的源码:
public int indexOf(Object elem) {
if (elem == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (elem.equals(elementData[i]))
return i;
}
return -1;
}
四、 总结
通过上述微小的错误暴露了编程中经常被忽略的细节,需要注意的是:
1. List的contains方法调用的是重写(override)Object的equals方法;
2. 子类重写(override)Object方法的equals方法时必须保证参数为Object;
3. 子类子类重写(override)Object方法的equals方法的同时通常也应重写hashCode方法。
分享到:
相关推荐
- **`addAll(int index, Collection<? extends E> c)`**:从指定位置开始,将指定集合中的所有元素插入列表中。 #### 删除元素 - **`remove(int index)`**:删除指定位置的元素,并返回该元素。`ArrayList`会通过...
总结来说,要在JNI中操作ArrayList对象并添加int类型的数据,你需要: 1. 定义JNI函数并获取ArrayList对象的本地引用。 2. 获取ArrayList类和`add`方法的ID。 3. 创建一个Integer对象,将int值包装进去。 4. 调用...
- `Contains(object item)`: 检查ArrayList是否包含指定的元素。 - `ToArray(Type type)`: 将ArrayList转换为指定类型的数组。 3. **ArrayList的性能** - ArrayList的插入和删除操作在数组的中间位置时,效率较...
为了提供更丰富的功能,还可以实现更多的方法,如清空ArrayList的`clear`方法、检查是否包含特定值的`contains`方法、合并两个ArrayList的`concat`方法等。 ```javascript ArrayList.prototype.clear = function() ...
在Android开发中,ArrayList是一个非常重要的数据结构,它属于Java集合框架的一部分,但在Android环境中被广泛使用。ArrayList关键字查询是Android应用中常见的功能,尤其在显示大量数据的ListView中,用户通常需要...
在实际开发中,这种方法特别适用于需要将从数据库或其他源获取的非结构化数据(如ArrayList)转换为可以进一步处理或绑定到控件(如DataGridView)的结构化数据(如DataTable)。此外,.NET Framework还提供了其他...
ArrayList关键字查询在Java编程中是一种常见的数据操作,特别是在与用户界面如ListView交互时。ArrayList是Java集合框架的一部分,它是一个动态数组,允许我们存储、添加、删除和查找对象。在这个"ArrayList关键字...
### ArrayList的使用详解 #### 一、什么是ArrayList? `ArrayList`是.NET框架中提供的一种动态数组类型,它属于`System.Collections`命名空间。与普通的数组相比,`ArrayList`具有更强大的功能,比如它可以动态地...
总结来说,使用ArrayList填充DataGrid涉及以下几个关键步骤: 1. 创建ArrayList并添加对象。 2. 将ArrayList设置为DataGrid的DataSource。 3. 指定DataGrid的列,映射ArrayList中对象的属性。 4. 在窗体中显示...
一个C++(Ubuntu16.04+QT5.9.1)通过JNI,调用JAVA类及方法的示例。通过JNI传递和返回多种类型的参数,boolean ,int,String,ArrayList,ArrayList嵌套ArrayList<ArrayList<String>>等。
总的来说,ArrayList集合工具类是Java编程中的核心组件,它在存储和操作有序数据集方面发挥着重要作用。在JavaScript开发中,虽然没有直接的对应物,但我们可以通过理解ArrayList的工作原理,利用Array对象来实现...
ArrayList类是Java编程语言中一个常用的集合类,它属于Java集合框架的一部分,位于`java.util`包中。ArrayList是一个基于数组实现的动态列表,可以存储任何类型的数据,并且支持动态扩容。在本实例中,我们将深入...
### C# 中 Array、ArrayList 和 List 的区别 在C#编程语言中,处理集合数据时,程序员经常需要根据实际需求选择合适的集合类型。本文将详细解释C#中Array、ArrayList和List之间的区别,并通过示例代码帮助理解这些...
总的来说,本ArrayList演示将通过实例展示ArrayList的各种操作,帮助开发者更好地理解和运用ArrayList,提升Java编程技能。通过学习和实践这些例子,你可以更熟练地处理基于数组的动态列表需求。
在C语言中,ArrayList是一种常见的数据结构,它模拟了Java或.NET等高级语言中的动态数组。ArrayList提供了在数组中添加、删除和查找元素的便利操作,而无需预先知道数组的大小。下面,我们将深入探讨如何用C语言实现...
虽然不如Java中的`ArrayList`那么方便,但它提供了一种灵活的方式来管理和操作动态数据集,对于C程序员来说是一个有价值的工具。通过学习和理解这个实现,开发者可以更好地理解和运用C语言的数据结构和内存管理技巧...
3. public virtual void RemoveRange(int index, int count):从 ArrayList 中移除一定范围的元素。 Example: ```csharp ArrayList aList = new ArrayList(); aList.Add("a"); aList.Add("b"); aList.Add("c"); a...
ArrayList的removeAll方法是一个常用的集合操作方法,该方法可以从一个ArrayList中删除所有在另外一个集合中的元素。但是,在实际开发过程中,removeAll方法的使用需要 thận重,因为它可能会导致性能问题。 1. ...
### ASP .NET - ArrayList对象 在ASP .NET中,`ArrayList`对象是一种非常有用的集合类,它可以存储任意类型的对象,并且大小是动态...理解`ArrayList`的工作原理及其用法,对于任何ASP .NET开发者来说都是非常重要的。
使用`contains()`方法可以判断ArrayList是否包含特定元素。 ```java boolean hasElement = list.contains("NewElement"); // 如果包含"NewElement"返回true ``` 7. **遍历ArrayList** 可以通过增强for循环...