`
qwind
  • 浏览: 11860 次
  • 性别: Icon_minigender_1
  • 来自: 镇江
社区版块
存档分类
最新评论

从ArrayList的contains说开来

阅读更多

从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方法。

 

 

分享到:
评论

相关推荐

    arrayList原理说明

    - **`addAll(int index, Collection&lt;? extends E&gt; c)`**:从指定位置开始,将指定集合中的所有元素插入列表中。 #### 删除元素 - **`remove(int index)`**:删除指定位置的元素,并返回该元素。`ArrayList`会通过...

    集合ArrayList测试集合ArrayList测试集合ArrayList测试

    `ArrayList`作为`List`接口的一个实现,其特点是元素按索引顺序存储,索引是从0开始的整数。 现在,让我们详细探讨`ArrayList`的一些关键特性: 1. **动态增长**:`ArrayList`在创建时不需要指定大小,它可以随着...

    jni操作arraylist对象

    总结来说,要在JNI中操作ArrayList对象并添加int类型的数据,你需要: 1. 定义JNI函数并获取ArrayList对象的本地引用。 2. 获取ArrayList类和`add`方法的ID。 3. 创建一个Integer对象,将int值包装进去。 4. 调用...

    你必须知道的C# ArrayList

    - `Contains(object item)`: 检查ArrayList是否包含指定的元素。 - `ToArray(Type type)`: 将ArrayList转换为指定类型的数组。 3. **ArrayList的性能** - ArrayList的插入和删除操作在数组的中间位置时,效率较...

    java中ArrayList的用法

    3. **从集合创建`ArrayList`**:可以直接从另一个集合创建一个新的`ArrayList`。 ```java Collection&lt;String&gt; collection = ...; ArrayList&lt;String&gt; list = new ArrayList(collection); ``` #### 三、添加和...

    JavaScript 实现基础 ArrayList 功能

    为了提供更丰富的功能,还可以实现更多的方法,如清空ArrayList的`clear`方法、检查是否包含特定值的`contains`方法、合并两个ArrayList的`concat`方法等。 ```javascript ArrayList.prototype.clear = function() ...

    Android ArrayList关键字查询.rar

    在Android开发中,ArrayList是一个非常重要的数据结构,它属于Java集合框架的一部分,但在Android环境中被广泛使用。ArrayList关键字查询是Android应用中常见的功能,尤其在显示大量数据的ListView中,用户通常需要...

    ArrayList转化为DataTable

    在实际开发中,这种方法特别适用于需要将从数据库或其他源获取的非结构化数据(如ArrayList)转换为可以进一步处理或绑定到控件(如DataGridView)的结构化数据(如DataTable)。此外,.NET Framework还提供了其他...

    ArrayList关键字查询demo

    ArrayList关键字查询在Java编程中是一种常见的数据操作,特别是在与用户界面如ListView交互时。ArrayList是Java集合框架的一部分,它是一个动态数组,允许我们存储、添加、删除和查找对象。在这个"ArrayList关键字...

    arraylist用法

    ### ArrayList的使用详解 #### 一、什么是ArrayList? `ArrayList`是.NET框架中提供的一种动态数组类型,它属于`System.Collections`命名空间。与普通的数组相比,`ArrayList`具有更强大的功能,比如它可以动态地...

    使用对象ArrayList填充DataGrid,C#源代码ArrayList MyList = new ArrayList();

    总结来说,使用ArrayList填充DataGrid涉及以下几个关键步骤: 1. 创建ArrayList并添加对象。 2. 将ArrayList设置为DataGrid的DataSource。 3. 指定DataGrid的列,映射ArrayList中对象的属性。 4. 在窗体中显示...

    ArrayList集合工具类

    总的来说,ArrayList集合工具类是Java编程中的核心组件,它在存储和操作有序数据集方面发挥着重要作用。在JavaScript开发中,虽然没有直接的对应物,但我们可以通过理解ArrayList的工作原理,利用Array对象来实现...

    ArrayList类操作程序实例

    ArrayList类是Java编程语言中一个常用的集合类,它属于Java集合框架的一部分,位于`java.util`包中。ArrayList是一个基于数组实现的动态列表,可以存储任何类型的数据,并且支持动态扩容。在本实例中,我们将深入...

    JNI与C++数据类型传递示例(包括ArrayList对象、ArrayList嵌套返回)

    一个C++(Ubuntu16.04+QT5.9.1)通过JNI,调用JAVA类及方法的示例。通过JNI传递和返回多种类型的参数,boolean ,int,String,ArrayList,ArrayList嵌套ArrayList&lt;ArrayList&lt;String&gt;&gt;等。

    C# Array和ArrayList,List区别

    ### C# 中 Array、ArrayList 和 List 的区别 在C#编程语言中,处理集合数据时,程序员经常需要根据实际需求选择合适的集合类型。本文将详细解释C#中Array、ArrayList和List之间的区别,并通过示例代码帮助理解这些...

    ArrayList演示

    总的来说,本ArrayList演示将通过实例展示ArrayList的各种操作,帮助开发者更好地理解和运用ArrayList,提升Java编程技能。通过学习和实践这些例子,你可以更熟练地处理基于数组的动态列表需求。

    用C语言模拟ArrayList

    在C语言中,ArrayList是一种常见的数据结构,它模拟了Java或.NET等高级语言中的动态数组。ArrayList提供了在数组中添加、删除和查找元素的便利操作,而无需预先知道数组的大小。下面,我们将深入探讨如何用C语言实现...

    c版的arraylist

    虽然不如Java中的`ArrayList`那么方便,但它提供了一种灵活的方式来管理和操作动态数据集,对于C程序员来说是一个有价值的工具。通过学习和理解这个实现,开发者可以更好地理解和运用C语言的数据结构和内存管理技巧...

    C#ArrayList的详细用法

    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...

    Java中ArrayList的removeAll方法详解

    ArrayList的removeAll方法是一个常用的集合操作方法,该方法可以从一个ArrayList中删除所有在另外一个集合中的元素。但是,在实际开发过程中,removeAll方法的使用需要 thận重,因为它可能会导致性能问题。 1. ...

Global site tag (gtag.js) - Google Analytics