Java集合源码学习(二)ArrayList分析

简介:

1.关于ArrayList

ArrayList直接继承AbstractList,实现了List、 RandomAccess、Cloneable、Serializable接口,
为什么叫"ArrayList",因为ArrayList内部是用一个数组存储元素值,相当于一个可变大小的数组,也就是动态数组。

(1)继承和实现
继承了AbstractList,实现了List:ArrayList是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
实现RandmoAccess接口:即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
实现了Cloneable接口:即覆盖了函数clone(),能被克隆。
实现java.io.Serializable接口:这意味着ArrayList支持序列化,能通过序列化去传输。

(2)线程安全
ArrayList中的操作不是线程安全的。建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。

 

2.常用的方法

(1)方法说明
boolean add(E e) 
将指定的元素添加到此列表的尾部。 
void add(int index, E element) 
将指定的元素插入此列表中的指定位置。 
boolean addAll(Collection<? extends E> c) 
按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。 
boolean addAll(int index, Collection<? extends E> c) 
从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。 
void clear() 
移除此列表中的所有元素。 
Object clone() 
返回此 ArrayList 实例的浅表副本。 
boolean contains(Object o) 
如果此列表中包含指定的元素,则返回 true。 
void ensureCapacity(int minCapacity) 
如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数所指定的元素数。 
E get(int index) 
返回此列表中指定位置上的元素。 
int indexOf(Object o) 
返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。 
boolean isEmpty() 
如果此列表中没有元素,则返回 true 
int lastIndexOf(Object o) 
返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。 
E remove(int index) 
移除此列表中指定位置上的元素。 
boolean remove(Object o) 
移除此列表中首次出现的指定元素(如果存在)。 
protected void removeRange(int fromIndex, int toIndex) 
移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素。 
E set(int index, E element) 
用指定的元素替代此列表中指定位置上的元素。 
int size() 
返回此列表中的元素数。 
Object[] toArray() 
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。 
void trimToSize() 
将此 ArrayList 实例的容量调整为列表的当前大小。应用程序可以使用此操作来最小化 ArrayList 实例的存储量。

(2)遍历方式
ArrayList支持3种遍历方式

1.通过迭代器遍历。即通过Iterator去遍历
2.随机访问,通过索引值去遍历,
ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素
3.for循环遍历

下面比较这3种方式的效率,代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public  static  void  main(String[] args){
         ArrayList<Object> list= new  ArrayList<>();
         for ( int  i= 0 ;i< 10000 ;i++){
             list.add(i);
         }
         useIterator(list);
         useRandomAccess(list);
         useForeach(list);
     }
     
     public  static  void  useIterator(List<Object> list){
         Long startTime = System.currentTimeMillis();
         Iterator iter = list.iterator();
         while  (iter.hasNext()) {
             iter.next();
         }
         System.out.println(System.currentTimeMillis()-startTime);
     }
     
     public  static  void  useRandomAccess(List<Object> list){
         Long startTime = System.currentTimeMillis();
         for  ( int  i= 0 ; i<list.size(); i++) {
             list.get(i);
             }
         System.out.println(System.currentTimeMillis()-startTime);
     }
     
     public  static  void  useForeach(List<Object> list){
         
         Long startTime = System.currentTimeMillis();
         for  (Object obj:list) {
                  ;
         }
         System.out.println(System.currentTimeMillis()-startTime);
     }

输出结果没放,不过结论是遍历ArrayList时,使用随机访问(即通过索引序号访问)效率最高,而使用迭代器的效率相对较低。

 

3.源码分析

在几个常用的集合实现类中,ArrayList的实现代码是最短的,比较简单。
(1)构造方法
ArrayList提供了三个构造方法:

//初始默认大小为10

1
2
3
public  ArrayList() {
this ( 10 );
}

//可以直接设置ArrayLi的初始大小

1
2
3
4
5
6
7
8
9
public  ArrayList( int  i){
if  (i <  0 ) {
throw  new  IllegalArgumentException(( new  StringBuilder())
.append( "Illegal Capacity: " ).append(i).toString());
else  {
elementData =  new  Object[i];
return ;
}
}

//使用Collettion作为参数初始化

1
2
3
4
5
6
7
public  ArrayList(Collection collection)
{
elementData = collection.toArray();
size = elementData.length;
if (((Object) (elementData)).getClass() != [Ljava/lang/Object;)
elementData = Arrays.copyOf(elementData, size, [Ljava/lang/Object;);
};

一个是无参数的构造方法,另外一个是拥有单个参数(类型为Collettion)的构造方法。

ArrayList还提供了第三个构造方法,其接受一个int值,用于设置ArrayLi的初始大小(默认大小为10)。

(2)数据结构

1
2
private  transient  Object elementData[];
private  int  size;

ArrayList内部维护了一个Object数组,可以通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,size 则是动态数组的实际大小。

(3)elementData[]为什么使用 transient 修饰?
被transient()定义的变量不可序列化,但是注意ArrayList同时实现了Serializable接口接口,这是为什么?
主要是为了提高序列化时的效率。
序列化有2种方式: 
1.只是实现了Serializable接口。 
序列化时,调用java.io.ObjectOutputStream的defaultWriteObject方法,将对象序列化。 
注意:此时transient修饰的字段,不会被序列化。 
2.实现了Serializable接口,同时提供了writeObject方法。 
序列化时,会调用该类的writeObject方法。而不是java.io.ObjectOutputStream的defaultWriteObject方法。 
注意:此时transient修饰的字段,是否会被序列化,取决于writeObject。 
假如现在实际有了5个元素,而elementData的大小可能是10,那么在序列化时只需要储存5个元素,数组中的最后五个元素是没有实际意义的,不需要储存。
所以ArrayList的设计者将elementData设计为transient,然后在writeObject方法中手动将其序列化,并且只序列化了实际存储的那些元素,而不是整个数组。

(4)源码注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
      * 使用一个Collection进行构造
      * Arraylist中大量使用了Arrays的相关方法
      */
     public  ArrayList(Collection collection)
     {
         elementData = collection.toArray();
         size = elementData.length;
         if ((Object) (elementData).getClass() != Object. class ){
             elementData = Arrays.copyOf(elementData, size, (elementData).getClass());
         }
     }
 
     /**
      * 如果当前数组的容量大于数组实际存储的数据元素数量,重新调整数组大小
      * 用于把ArrayList的容量缩减到当前实际大小,减少存储容量。
      * 其中的变量modCount由AbstracList继承而来,记录List发生结构化修改(structurally modified)的次数。
      * @Title: trimToSize
      */
     public  void  trimToSize() {
         modCount++;
         int  i = elementData.length;
         if  (size < i)
             elementData = Arrays.copyOf(elementData, size);
     }
 
/**
      * 确保ArrayList的大小
      * @Title: ensureCapacity
      * @param i
      */
     public  void  ensureCapacity( int  i) {
         modCount++;
         int  j = elementData.length;
         if  (i > j) {
             Object aobj[] = elementData;
             int  k = (j *  3 ) /  2  1 ;
             if  (k < i)
                 k = i;
             elementData = Arrays.copyOf(elementData, k);
         }
     }
 
/**
      * 返回指定元素的下标,要区分参数是否为null
      * 注意ArrayList可以是可以存储null的
      */
     public  int  indexOf(Object obj) {
         if  (obj ==  null ) {
             for  ( int  i =  0 ; i < size; i++)
                 if  (elementData[i] ==  null )
                     return  i;
 
         else  {
             for  ( int  j =  0 ; j < size; j++)
                 if  (obj.equals(elementData[j]))
                     return  j;
 
         }
         return  - 1 ;
     }
/**
      * 使用了系统的System.arraycopy方法
      * 这是一个native的方法
      */
     public  Object[] toArray(Object aobj[]) {
         if  (aobj.length < size)
             return  (Object[]) Arrays.copyOf(elementData, size,(aobj).getClass());
         System.arraycopy(((Object) (elementData)),  0 , ((Object) (aobj)),  0 ,
                 size);
         if  (aobj.length > size)
             aobj[size] =  null ;
         return  aobj;
     }
 
/**
      * 和序列化以及transient的处理有关系
      */
     private  void  writeObject(ObjectOutputStream objectoutputstream)
             throws  IOException {
         int  i = modCount;
         objectoutputstream.defaultWriteObject();
         objectoutputstream.writeInt(elementData.length);
         for  ( int  j =  0 ; j < size; j++)
             objectoutputstream.writeObject(elementData[j]);
 
         if  (modCount != i)
             throw  new  ConcurrentModificationException();
         else
             return ;
     }

  

4.总结

在Java中应用静态数组很容易发生一些错误,相比较来说,

ArrayList可以动态的增加和减少元素,灵活的设置数组的大小,提供了多种遍历方式,在设计时就尽可能的考虑了性能,大多数工程开发中数组都会使用ArrayList。

 



本文转自邴越博客园博客,原文链接:http://www.cnblogs.com/binyue/p/5229937.html,如需转载请自行联系原作者

相关文章
|
25天前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
247 1
|
1月前
|
数据采集 搜索推荐 Java
Java 大视界 -- Java 大数据在智能教育虚拟学习环境构建与用户体验优化中的应用(221)
本文探讨 Java 大数据在智能教育虚拟学习环境中的应用,涵盖多源数据采集、个性化推荐、实时互动优化等核心技术,结合实际案例分析其在提升学习体验与教学质量中的成效,并展望未来发展方向与技术挑战。
|
2月前
|
并行计算 Java API
Java List 集合结合 Java 17 新特性与现代开发实践的深度解析及实战指南 Java List 集合
本文深入解析Java 17中List集合的现代用法,结合函数式编程、Stream API、密封类、模式匹配等新特性,通过实操案例讲解数据处理、并行计算、响应式编程等场景下的高级应用,帮助开发者提升集合操作效率与代码质量。
124 1
|
2月前
|
前端开发 Java API
新手 Java 学习资料结合最新技术的精选推荐及高效学习资源参考
本文为新手推荐了涵盖Java基础到最新技术的学习资料,包括官方文档、在线课程、书籍、学习网站及实践平台,帮助系统掌握Java编程,并通过Spring Boot实战提升开发能力。
111 1
|
1月前
|
存储 搜索推荐 安全
Java 大视界 --Java 大数据在智能教育学习效果评估与教学质量改进中的应用(209)
本文探讨了 Java 大数据在智能教育中的创新应用,涵盖学习效果评估、教学质量改进及个性化教学方案定制等内容,结合实战案例与代码解析,展现技术如何赋能教育智能化转型。
|
2月前
|
存储 缓存 NoSQL
java 集合入门基础理论的核心概念与实用长尾知识
本文介绍了Java集合框架的基础理论知识,包括单列集合(List、Set、Queue)和双列集合(Map)的特点及常用实现类(如ArrayList、HashSet、HashMap等)。详细讲解了集合的遍历方式(迭代器、增强for循环、Lambda表达式)和典型应用场景(如数据去重、键值存储等)。通过具体代码示例,帮助初学者理解集合框架的核心概念和实际应用,为Java编程中的数据存储与管理提供基础指导。
73 0
|
2月前
|
存储 Java 大数据
Java 大视界 -- Java 大数据在智能家居能源消耗模式分析与节能策略制定中的应用(198)
简介:本文探讨Java大数据技术在智能家居能源消耗分析与节能策略中的应用。通过数据采集、存储与智能分析,构建能耗模型,挖掘用电模式,制定设备调度策略,实现节能目标。结合实际案例,展示Java大数据在智能家居节能中的关键作用。
|
2月前
|
安全 Java API
Java 集合高级应用与实战技巧之高效运用方法及实战案例解析
本课程深入讲解Java集合的高级应用与实战技巧,涵盖Stream API、并行处理、Optional类、现代化Map操作、不可变集合、异步处理及高级排序等核心内容,结合丰富示例,助你掌握Java集合的高效运用,提升代码质量与开发效率。
181 0
|
2月前
|
存储 安全 Java
Java 学习路线 35 掌握 List 集合从入门到精通的 List 集合核心知识
本文详细解析Java中List集合的原理、常用实现类(如ArrayList、LinkedList)、核心方法及遍历方式,并结合数据去重、排序等实际应用场景,帮助开发者掌握List在不同业务场景下的高效使用,提升Java编程能力。
280 0
|
存储 Java 索引
【Java集合类面试二十四】、ArrayList和LinkedList有什么区别?
ArrayList基于动态数组实现,支持快速随机访问;LinkedList基于双向链表实现,插入和删除操作更高效,但占用更多内存。

热门文章

最新文章