`
yiminghe
  • 浏览: 1460793 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

ArrayList 实现(转载)

    博客分类:
  • java
 
阅读更多

ArrayList是List接口的一个可变长数组实现。实现了所有List接口的操作,并允许存储null值。除了没有进行同 步,ArrayList基本等同于Vector。在Vector中几乎对所有的方法都进行了同步,但ArrayList仅对writeObject和 readObject进行了同步,其它比如add(Object)、remove(int)等都没有同步。

1.存储
ArrayList使用一个Object的数组存储元素。
private transient Object elementData[];
ArrayList实现了java.io.Serializable接口,这儿的transient标示这个属性不需要自动序列化。下面会在writeObject()方法中详细讲解为什么要这样作。

2.add和remove

public boolean add(Object o) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = o;
return true;
} 
 



注意这儿的ensureCapacity()方法,它的作用是保证elementData数组的长度可以容纳一个新元素。在“自动变长机制”中将详细讲解。

public Object remove(int index) {
RangeCheck(index);
modCount++;
Object oldValue = elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
} 
 



RangeCheck()的作用是进行边界检查。由于ArrayList采用一个对象数组存储元素,所以在删除一个元素时需要把后面的元素前移。删除一个元素时只是把该元素在elementData数组中的引用置为null,具体的对象的销毁由垃圾收集器负责。
modCount的作用将在下面的“iterator()中的同步”中说明。
注: 在前移时使用了System提供的一个实用方法:arraycopy(),在本例中可以看出System.arraycopy()方法可以对同一个数组进 行操作,这个方法是一个native方法,如果对同一个数组进行操作时,会首先把从源部分拷贝到一个临时数组,在把临时数组的元素拷贝到目标位置。

3.自动变长机制

在实例化一个ArrayList时,你可以指定一个初始容量。这个容量就是elementData数组的初始长度。如果你使用:

ArrayList list = new ArrayList();

则使用缺省的容量:10。

public ArrayList() {
this(10);
}

ArrayList提供了四种add()方法,

public boolean add(Object o)

public void add(int index, Object element)

public boolean addAll(Collection c)

public boolean addAll(int index, Collection c) 
 



在每一种add()方法中,都首先调用了一个ensureCapacity(int miniCapacity)方法,这个方法保证elementData数组的长度不小于miniCapacity。ArrayList的自动变长机制就是在这个方法中实现的。

public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = new Object[newCapacity];
System.arraycopy(oldData, 0, elementData, 0, size);
}
} 
 



从这个方法实现中可以看出ArrayList每次扩容,都扩大到原来大小的1.5倍。
每种add()方法的实现都大同小异,下面给出add(Object)方法的实现:

public boolean add(Object o) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = o;
return true;
}
 



4.iterator()中的同步
在父类AbstractList中定义了一个int型的属性:modCount,记录了ArrayList结构性变化的次数。

protected transient int modCount = 0;

在ArrayList的所有涉及结构变化的方法中都增加modCount的值,包括:add()、remove()、addAll()、removeRange()及clear()方法。这些方法每调用一次,modCount的值就加1。
注:add()及addAll()方法的modCount的值是在其中调用的ensureCapacity()方法中增加的。

AbstractList中的iterator()方法(ArrayList直接继承了这个方法)使用了一个私有内部成员类Itr,生成一个Itr对象(Iterator接口)返回:

public Iterator iterator() {
return new Itr();
}

Itr实现了Iterator()接口,其中也定义了一个int型的属性:expectedModCount,这个属性在Itr类初始化时被赋予ArrayList对象的modCount属性的值。

int expectedModCount = modCount;

注:内部成员类Itr也是ArrayList类的一个成员,它可以访问所有的AbstractList的属性和方法。理解了这一点,Itr类的实现就容易理解了。

在Itr.hasNext()方法中:

public boolean hasNext() {
return cursor != size();
}

调用了AbstractList的size()方法,比较当前光标位置是否越界。

在Itr.next()方法中,Itr也调用了定义在AbstractList中的get(int)方法,返回当前光标处的元素:

public Object next() {
try {
Object next = get(cursor);
checkForComodification();
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
} 
 


注意,在next()方法中调用了checkForComodification()方法,进行对修改的同步检查:

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
 


现 在对modCount和expectedModCount的作用应该非常清楚了。在对一个集合对象进行跌代操作的同时,并不限制对集合对象的元素进行操 作,这些操作包括一些可能引起跌代错误的add()或remove()等危险操作。在AbstractList中,使用了一个简单的机制来规避这些风险。 这就是modCount和expectedModCount的作用所在。

5.序列化支持
ArrayList实现了java.io.Serializable接口,所以ArrayList对象可以序列化到持久存储介质中。ArrayList的主要属性定义如下:

private static final long serialVersionUID = 8683452581122892189L;

private transient Object elementData[];

private int size;

可 以看出serialVersionUID和size都将自动序列化到介质中,但elementData数组对象却定义为transient了。也就是说 ArrayList中的所有这些元素都不会自动系列化到介质中。为什么要这样实现?因为elementData数组中存储的“元素”其实仅是对这些元素的 一个引用,并不是真正的对象,序列化一个对象的引用是毫无意义的,因为序列化是为了反序列化,当你反序列化时,这些对象的引用已经不可能指向原来的对象 了。所以在这儿需要手工的对ArrayList的元素进行序列化操作。这就是writeObject()的作用。

private synchronized void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
s.defaultWriteObject();
// Write out array length
s.writeInt(elementData.length);
// Write out all elements in the proper order.
for (int i=0; i<size; i++)
s.writeObject(elementData[i]);
} 
 



这样元素数组elementData中的所以元素对象就可以正确地序列化到存储介质了。
对应的readObject()也按照writeObject()方法的顺序从输入流中读取:

private synchronized void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in array length and allocate array
int arrayLength = s.readInt();
elementData = new Object[arrayLength];
// Read in all elements in the proper order.
for (int i=0; i<size; i++)
elementData[i] = s.readObject();
} 
 

 

 
分享到:
评论

相关推荐

    arraylist 类泛函实现内幕

    arraylist 类泛函实现内幕 让大家看看那个arraylist 怎么实现的 泛函是个新东西,所以呢错误难免。 如果代码有问题,请大家谅解,帮我完善他! 我的资源,你们可以转载。对于书籍,其版权归原书作者...

    数据挖掘贝叶斯——一种实现

    该类的实现同本人转载的一篇博文:对BigDecimal常用方法的归类中的Arith类相同。 数据挖掘贝叶斯算法的Java实现可以用于小规模数据集的实验和测试,但不适合用于工程应用。如果需要应用于大规模数据集或工程应用,...

    Java程序员面试的试题集(1_122)帮助初学者的技术问题(转载)

    `Vector` 与 `ArrayList` 相比,额外提供了线程安全的实现,但这通常会降低其性能。 - **LinkedList**:使用双向链表实现,不支持随机访问,但在插入和删除元素时表现出色,因为仅需调整链表中的链接,无需移动...

    [转载]+[C#]+加强型音乐播放器+代码类

    音乐播放器还可能包含播放列表管理功能,涉及数据结构和集合的使用,例如ArrayList或List用于存储歌曲信息,包括文件路径、艺术家、专辑等元数据。此外,可能还有搜索和排序功能,需要用到字符串处理和排序算法。 ...

    商品管理系统.zip

    描述:本项目纯使用ArrayList...注册登录中有多种逻辑验证,实现用户名密码等的输入验证,逻辑简单但是验证相对完善,商品管理系统中有商品的简单的怎删改查(ArrayList存储)! 声明:本项目仅允许学习使用,不得转载

    java编程事项(转载收集整理版)

    4. **集合框架**:Java集合框架包括List(如ArrayList和LinkedList)、Set(如HashSet和TreeSet)和Map(如HashMap和TreeMap)。熟练掌握它们的操作,如添加、删除、遍历以及查找元素,可以大大提高代码效率。 5. *...

    JDBC工具类(针对mySQL)

    旨在方便用户将数据库的内容转化为Vector、ArrayList容器的操作过程,此类中提供了多种方法从而简化了对ResultSet结果集的转化成本,结合系统提供的ResultSetMetaData类实现了通过结果集查询表列数目、名称、属性...

    通过JavaCompiler进行编译java文件(转载)

    总的来说,通过JavaCompiler API,开发者可以轻松地在Java应用程序中实现动态编译的功能,这对于需要在运行时生成或修改代码的项目来说是一大利器。不过,需要注意的是,这种编译方式可能会增加程序的复杂性,并且在...

    Json-RPC for java Example

    json-rpc-for-java,是仅仅不到100行的javascript代码和不到10个java文件实现的超级轻量级的通过 javaScript快速调用java对象并返回任意对象的轻量级框架,并且支持级联调用,也就是说不需要额外 的JavaScript编程,...

    Java 最常见 200+ 面试题全解析:面试必备.pdf

    2. 容器:包括List、Set、Map等接口的实现类和特点,例如ArrayList、LinkedList、HashMap、TreeMap等,并探讨它们的性能、使用场景。 3. 多线程:介绍线程的创建和管理,线程同步机制,如synchronized关键字,wait...

    抽奖软件java

    Java的ArrayList或ArrayDeque等集合类也可以作为替代,提供更灵活的操作。 5. **随机数生成**:抽奖过程通常需要生成随机数,Java的`java.util.Random`类可以用于此目的。开发者可能会使用这个类来随机选择获奖者,...

    Java面试资料大集合

    - **ArrayList和LinkedList**:它们的实现方式、性能特点及适用场景。 - **HashMap和HashSet**:底层实现原理,线程安全问题及如何解决。 - **TreeMap和TreeSet**:红黑树的特性,排序规则。 3. **多线程** - *...

    Java面试题

    3. **集合框架**:重点掌握ArrayList、LinkedList、HashSet、HashMap等常见集合的内部结构和操作原理。了解List、Set、Map接口以及它们之间的区别。同时,理解并发容器如ConcurrentHashMap和CopyOnWriteArrayList的...

    java事例集合1

    【描述】中的"事例集合1(转载)"表明这些内容可能来源于网络,可能是某个开发者或教育者收集并整理的Java编程实例,目的是帮助学习者通过实际操作来理解Java编程。而"看韩剧www.pigkrtv.com"这部分看起来像是无关的...

    java编程思想习题及答案

    3. **数组与集合**:了解如何声明、初始化和操作数组,以及掌握ArrayList、LinkedList、HashSet、HashMap等集合框架的使用。习题可能包含数组排序、查找元素或实现特定集合功能的挑战。 4. **异常处理**:学习Java...

    二十三种设计模式【PDF版】

    可扩展的使用 JDBC针对不同的数据库编程,Facade提供了一种灵活的实现. 设计模式之 Composite(组合) 就是将类用树形结构组合成一个单位.你向别人介绍你是某单位,你是单位中的一个元素,别人和你做买卖,相当于 和...

    收集的常见的专业问题解决办法.rar

    2009-02-24 08:31 61003 61003 常见的专业问题解决办法\Java容器类List、ArrayList、Vector及map、HashTable、HashMap的使用与区别.rar 2009-02-24 08:29 40960 13763 常见的专业问题解决办法\java容器类介绍.doc ...

    springmybatis

    mybatis实战教程mybatis in action之三实现数据的增删改查 mybatis实战教程mybatis in action之四实现关联数据的查询 mybatis实战教程mybatis in action之五与spring3集成附源码 mybatis实战教程mybatis in action之...

Global site tag (gtag.js) - Google Analytics