实现原理

前面已经提了一下Collection的实现基础都是基于数组的。下面我们就已ArrayList 为例,简单分析一下ArrayList 列表的实现方式。首先,先看下它的构造函数。

下列表格是在SUN提供的API中的描述:

ArrayList()           Constructs an empty list with an initial capacity of ten.

ArrayList(Collection c)           Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.

ArrayList(int initialCapacity)           Constructs an empty list with the specified initial capacity.

其中第一个构造函数ArrayList()和第二构造函数ArrayList(Collection c) 是按照Collection 接口文档所述,所应该提供两个构造函数,一个无参数,一个接受另一个 Collection。

第3个构造函数:

ArrayList(int initialCapacity) 是ArrayList实现的比较重要的构造函数,虽然,我们不常用它,但是某认的构造函数正是调用的该带参数:initialCapacity 的构造函数来实现的。 其中参数:initialCapacity 表示我们构造的这个ArrayList列表的初始化容量是多大。如果调用默认的构造函数,则表示默认调用该参数为initialCapacity =10 的方式,来进行构建一个ArrayList列表对象。

为了更好的理解这个initialCapacity 参数的概念,我们先看看ArrayList在Sun 提供的源码中的实现方式。先看一下它的属性有哪些:

ArrayList 继承了AbstractList 我们主要看看ArrayList中的属性就可以了。

ArrayList中主要包含2个属性:

private transient Object elementData[];

private int size;

其中数组::elementData[] 是列表的实现核心属性:数组。 我们使用该数组来进行存放集合中的数据。而我们的初始化参数就是该数组构建时候的长度,即该数组的length属性就是initialCapacity 参数。

Keys:transient 表示被修饰的属性不是对象持久状态的一部分,不会自动的序列化。

第2个属性:size表示列表中真实数据的存放个数。

我们再来看一下ArrayList的构造函数,加深一下ArrayList是基于数组的理解。

从源码中可以看到默认的构造函数调用的就是带参数的构造函数:

public ArrayList(int initialCapacity)

不过参数initialCapacity=10 。

我们主要看ArrayList(int initialCapacity) 这个构造函数。可以看到:

this.elementData = new Object[initialCapacity];

我们就是使用的initialCapacity 这个参数来创建一个Object数组。而我们所有的往该集合对象中存放的数据,就是存放到了这个Object数组中去了。

我们在看看另外一个构造函数的源码:

这里,我们先看size() 方法的实现形式。它的作用即是返回size属性值的大小。然后我们再看另外一个构造函数public ArrayList(Collection c) ,该构造函数的作用是把另外一个容器对象中的元素存放到当前的List 对象中。

可以看到,首先,我们是通过调用另外一个容器对象C 的方法size()来设置当前的List对象的size属性的长度大小。

接下来,就是对elementData 数组进行初始化,初始化的大小为原先容器大小的1.1倍。最后,就是通过使用容器接口中的Object[] toArray(Object[] a) 方法来把当前容器中的对象都存放到新的数组elementData 中。这样就完成了一个ArrayList 的建立。

可能大家会存在一个问题,那就是,我们建立的这个ArrayList 是使用数组来实现的,但是数组的长度一旦被定下来,就不能改变了。而我们在给ArrayList对象中添加元素的时候,却没有长度限制。这个时候,ArrayList 中的elementData 属性就必须存在一个需要动态的扩充容量的机制。我们看下面的代码,它描述了这个扩充机制:

这个方法的作用就是用来判断当前的数组是否需要扩容,应该扩容多少。其中属性:modCount是继承自父类,它表示当前的对象对elementData数组进行了多少次扩容,清空,移除等操作。该属性相当于是一个对于当前List 对象的一个操作记录日志号。 我们主要看下面的代码实现:

1.      首先得到当前elementData 属性的长度oldCapacity。

2.      然后通过判断oldCapacity和minCapacity参数谁大来决定是否需要扩容

n         如果minCapacity大于oldCapacity,那么我们就对当前的List对象进行扩容。扩容的的策略为:取(oldCapacity * 3)/2 + 1和minCapacity之间更大的那个。然后使用数组拷贝的方法,把以前存放的数据转移到新的数组对象中

n         如果minCapacity不大于oldCapacity那么就不进行扩容。

下面我们看看上的那个ensureCapacity方法的是如何使用的:

上的两个add方法都是往List 中添加元素。每次在添加元素的时候,我们就需要判断一下,是否需要对于当前的数组进行扩容。

我们主要看看  public boolean add(Object o)方法,可以发现在添加一个元素到容器中的时候,首先我们会判断是否需要扩容。因为只增加一个元素,所以扩容的大小判断也就为当前的size+1来进行判断。然后,就把新添加的元素放到数组elementData中。

第二个方法public boolean addAll(Collection c)也是同样的原理。将新的元素放到elementData数组之后。同时改变当前List 对象的size属性。

类似的List 中的其他的方法也都是基于数组进行操作的。大家有兴趣可以看看源码中的更多的实现方式。

最后我们再看看如何判断在集合中是否已经存在某一个对象的:

由源码中我们可以看到,public boolean contains(Object elem)方法是通过调用public int indexOf(Object elem)方法来判断是否在集合中存在某个对象elem。我们看看indexOf方法的具体实现。

首先我们判断一下elem 对象是否为null ,如果为null的话,那么遍历数组elementData 把第一个出现null的位置返回。

如果elem不为null 的话,我们也是遍历数组elementData ,并通过调用elem对象的equals()方法来得到第一个相等的元素的位置。

这里我们可以发现,ArrayList中用来判断是否包含一个对象,调用的是各个对象自己实现的equals()方法。在前面的高级特性里面,我们可以知道:如果要判断一个类的一个实例对象是否等于另外一个对象,那么我们就需要自己覆写Object类的public boolean equals(Object obj) 方法。如果不覆写该方法的话,那么就会调用Object的equals()方法来进行判断。这就相当于比较两个对象的内存应用地址是否相等了。

在集合框架中,不仅仅是List,所有的集合类,如果需要判断里面是否存放了的某个对象,都是调用该对象的equals()方法来进行处理的。

J2SE知识点摘记(二十一)的更多相关文章

  1. J2SE知识点摘记(二)

    1.    对象的声明 "类名 对象名 = new 类名();"例子:Person P;//先声明一个Person类的对象p p=new Person();//用new关键字实例化 ...

  2. J2SE知识点摘记(二十六)

    为了用“集合框架”的额外部分把排序支持添加到 Java 2 SDK,版本 1.2,核心 Java 库作了许多更改.像 String 和 Integer 类如今实现 Comparable 接口以提供自然 ...

  3. J2SE知识点摘记(二十五)

    Set 1.5.1        概述 Java 中的Set和正好和数学上直观的集(set)的概念是相同的.Set最大的特性就是不允许在其中存放的元素是重复的.根据这个特点,我们就可以使用Set 这个 ...

  4. J2SE知识点摘记(二十四)

     覆写hashCode() 在明白了HashMap具有哪些功能,以及实现原理后,了解如何写一个hashCode()方法就更有意义了.当然,在HashMap中存取一个键值对涉及到的另外一个方法为equa ...

  5. J2SE知识点摘记(二十三)

    我们简单介绍一下这个接口: 1.4.3        Comparable 接口 在 java.lang 包中,Comparable 接口适用于一个类有自然顺序的时候.假定对象集合是同一类型,该接口允 ...

  6. J2SE知识点摘记(二十二)

    Map 1.4.1        概述 数学中的映射关系在Java中就是通过Map来实现的.它表示,里面存储的元素是一个对(pair),我们通过一个对象,可以在这个映射关系中找到另外一个和这个对象相关 ...

  7. J2SE知识点摘记(二十)

    List 1.3.1        概述 前面我们讲述的Collection接口实际上并没有直接的实现类.而List是容器的一种,表示列表的意思.当我们不知道存储的数据有多少的情况,我们就可以使用Li ...

  8. J2SE知识点摘记-数据库(二)

    一.          查询数据 注意sql的内容. 通过ResultSet接口保存全部的查询结果,通过Statement接口中的executeQuery()方法查询.查询之后需要分别取出.通过nex ...

  9. J2SE知识点摘记(十二)

    1.      File类 下面的构造方法可以用来生成File对象 File(String directoryPath) geName()用于返回文件名,getParent()返回父目录名,exist ...

随机推荐

  1. 自写Date工具类

    以前写项目的时候总是在使用到了时间的转换的时候才在工具类中添加一个方法,这样很容易导致代码冗余以及转换的方法注释不清晰导致每次使用都要重新看一遍工具类.因此整理出经常使用的一些转换,用作记录,以便以后 ...

  2. JSP 适配手机屏幕

    <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scal ...

  3. 浏览器文档播放Shockwave Flash 插件问题

    浏览器被提示shockwave flash crashed怎么办?在使用浏览器的时候经常被提示shockwave flash crashed,flash插件崩溃,网页就会出现一些无法显示的文件,下面绿 ...

  4. 商品列表中显示类别名称而不是类别ID

    商品表中的字段包裹商品信息和categoryid 若要在商品列表中显示出categoryname,有两种做法: 第一种做法: 拿到categoryid后再跟数据库连接一下,然后拿出categoryna ...

  5. QT5.3无法自动调用incomingConnection函数的问题

    最近将qt4.7的一个工程移到5.3,遇到了几个麻烦事,主要是这个incomingConnection监听后无法自动调用的问题,在4.7上是完全没有问题的,到了5.3就不行,网上也查了下,网友们都是放 ...

  6. 给新建的Cocos2d-x 3.X的Win32工程添加CocoStudio库

    1.我们在VS中找到"解决方案资源管理器", 在解决方案"HelloCocos"上点击右键, 选择添加现有项目. 在弹出的对话框中选择************* ...

  7. 实用chrome插件

    2015年最实用的9款chrome插件 随着14年chrome浏览器的市场超过IE浏览器,chrome凭借它强劲性能和出色的使用体验真正的登上了平民级的殿堂.今天小编就为大家推荐9款自己常用的chro ...

  8. javascript 汉字拼音排序

    定义和用法 用本地特定的顺序来比较两个字符串. 语法 stringObject.localeCompare(target) 参数 描述 target 要以本地特定的顺序与 stringObject 进 ...

  9. 用Python写的一个多线程机器人聊天程序

    本人是从事php开发的, 近来想通过php实现即时通讯(兼容windows).后来发现实现起来特别麻烦, 就想到python.听说这家伙在什么地方都能发挥作用.所以想用python来做通讯模块...所 ...

  10. Struts2 四、Struts2 处理流程

    1. 一个请求在Struts2框架中的处理步骤: a) 客户端初始化一个指向Servlet容器的请求: b) 根据Web.xml配置,请求首先经过ActionContextCleanUp过滤器,其为可 ...