在上一章中,我们了解了Collection的建筑。本章一开始我们讲解Collection的具体实现类;首先我们解释一下List,在List中,ArrayList是最常用的。因此,本章我们讲解ArrayList。首先对ArrayList有一个整体的了解,然后学习它的源码,最后通过例子学习如何使用它。内容包括:
第 1 部分 ArrayList 简介
第 2 部分 ArrayList 数据结构
第 3 部分 ArrayList 源码分析(基于 JDK1.6.0_45)
第 4 部分 ArrayList 遍历方法 没有。 5 toArray() 异常的一部分
第 6 部分 ArrayList 示例
转载请注明出处:http://www.sychzs.cn/skywang12345/p/3308556.html
ArrayList简介
ArrayList是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量可以动态增长。它继承自AbstractList,并实现了List、RandomAccess、Cloneable、java.io.Serialized接口。
ArrayList 继承AbstractList并实现List。它是一个数组队列,提供增、删、改、遍历等相关功能。
ArrayList 实现 RandmoAccess 接口,提供随机访问功能。 RandmoAccess是java中List的实现,为List提供快速访问功能。在ArrayList中,我们可以通过元素的序号快速获取元素对象;这是快速随机访问。稍后我们将比较List的“快速随机访问”和“通过迭代器访问”的效率。
ArrayList实现了Cloneable接口,覆盖了函数clone(),可以被克隆。
ArrayList实现了java.io.Serialized接口,这意味着ArrayList支持序列化,可以通过序列化进行传输。
与 Vector 不同,ArrayList 中的操作不是线程安全的! 因此,建议仅在单线程中使用ArrayList,而在多线程中可以选择Vector或CopyOnWriteArrayList。
ArrayList 构造函数
//默认构造函数 ArrayList() //容量是ArrayList的默认容量大小。当由于添加数据而导致容量不足时,容量会按之前容量大小的一半添加。 ArrayList(int容量) // 创建一个包含集合的ArrayList ArrayList(Collection扩展E>集合)
ArrayList API
// Collection 定义的 API 布尔值添加(E对象) boolean addAll(Collection 扩展 E>集合) void 清除() 布尔值包含(对象对象) boolean containsAll(Collection>集合) 布尔值等于(对象对象)int hashCode() 布尔值 isEmpty() 迭代器迭代器() 布尔值删除(对象对象) booleanremoveAll(第八>集合) booleanretainAll(第八>集合) int 尺寸() T[] toArray(T[] 数组) 对象[] toArray() //抽象紫色中定义的API void添加(int位置,E对象) boolean addAll(int位置, 乔治 extends E>200 收藏) E get(int位置) intindexOf(对象对象) intlastIndexOf(对象对象) ListIterator listIterator(int位置) ListIterator listIterator() E 删除(int位置)E组(int位置,E对象) 列表 子列表(int开始,int结束) //ArrayList 的新 API 对象克隆() void确保容量(int最小容量) voidtrimToSize() voidVoidremoverange(Int从索引,InttoIndex)第 2 部分 ArrayList 数据结构
ArrayList的继承关系
java.lang.Object ↳ java.util.AbstractCollection↳ java.util.AbstractList ↳ java.util.ArrayList public class ArrayList 扩展 AbstractList 实现列表 、随机访问、可克隆、www.sychzs.cn.可序列化{} ArrayList与Collection的关系如下图:
ArrayList 包含两个重要的对象:elementData 和 size。
(01) elementData 是一个“Object[]类型的数组”,保存了添加到ArrayList中的元素。事实上,elementData是一个动态数组,我们可以使用构造函数ArrayList(int initialCapacity)来执行其初始容量为initialCapacity;如果通过不带参数的构造函数ArrayList()创建ArrayList,则elementData的容量默认为10。elementData数组的大小会随着ArrayList容量的增长而动态增长。具体增长方式请参考源码分析中的ensureCapacity()函数。
(02) size 是动态数组的实际大小。
第三部分ArrayList源码分析(基于JDK1.6.0_45)
为了更好的理解ArrayList的原理,下面我们来分析一下ArrayList源码。 ArrayList是通过数组实现的,源码比较容易理解。
查看代码1 包 java.util; 2 3 公共 类ArrayList扩展摘要列表 4 实现列表 、随机访问、可克隆、www.sychzs.cn.可序列化 5 { 6 // 序列号 7 私人静态最终 长serialVersionUID = 8683452581122892189L; 8 9 // ArrayList中保存数据的数组 10 私有 瞬态对象[]元素数据; 11 12 // ArrayList 中实际数据的数量 13 私人 int 尺寸; 14 15 // 具有容量大小的 ArrayList 构造函数。 16 publicArrayList(intinitialCapacity){ 17 超级(); 18 if(初始容量 < 0) 19 投掷 new IllegalArgumentException("非法容量:"+ 20 初始容量); 21 // 创建一个新数组 22 这个.elementData = new对象[初始容量]; 23 } 24 25 // ArrayList 构造函数。默认容量为 10。 26 public ArrayList() { 27 这个(10); 28 } 29 30 // 创建一个包含集合的ArrayList 31 公共ArrayList(Collection<?扩展E> c) { 32 elementData = c.toArray(); 33 大小 = elementData.length; 34 // c.toArray 可能(错误地)不返回 Object[](请参阅 6260652) 35 if (elementData.getClass() != Object[].class) 36 elementData = Arrays.copyOf(elementData, size, Object[].class); 37 } 38 39 40 // 设置当前容量值 = 实际元素数 41 公共voidtrimToSize(){ 42 modCount++; 43 intoldCapacity =elementData.length; 44 if(尺寸 < 旧容量){ 45 elementData = Arrays.copyOf(elementData, 大小); 46 } 47 } 48 49 50 // 确定ArrarList的容量。 51 // 如果ArrayList的容量不足以容纳当前所有元素,则设置新容量 = "(原容量 52 公共 void 确保容量(int) 最小容量){ 53 // 添加“修改统计”+1 54 modCount++; 55 intoldCapacity =elementData.length; 56 // 如果当前容量不足以容纳当前元素数量,则设置新容量=“(原容量x3)/2 + 1” 57 if(minCapacity > oldCapacity){ 58 对象 oldData[] = elementData; 59 int 新容量 = (旧容量 * 3)/2 + 1; 60 if(新容量 < 最小容量) 61 newCapacity = minCapacity; 62 elementData = Arrays.copyOf(elementData, newCapacity); 63 } 64 } 65 66 // 添加元素 e 67 公共 布尔值 添加(E e){ 68 // 确定ArrayList的容量 69 确保容量(大小 + 1); // 增加 modCount! 70 // 将 e 添加到 ArrayList 71 elementData[size++] = e; 72 返回 真; 73 } 74 75 // 返回ArrayList的实际大小 76 公共int尺寸(){ 77返回尺寸; 78 } 79 80 // 返回ArrayList是否包含Object(o) 81 公共布尔包含(对象o){ 82 返回indexOf(o) >= 0; 83 } 84 85 // 返回ArrayList是否为空 86 public boolean isEmpty() { 87 返回尺寸== 0; 88 } 89 90 //向前查找,返回元素的索引值 91 public intindexOf(对象o){ 92 if(o == null){ 93 对于 (int i = 0; i < 大小; i++) 94 if(elementData[i]==null) 95 返回; 96 } 其他 { 97 对于 (int i = 0; i < 大小; i++) 98 if (o.equals(elementData[i])) 99返回i; 100 } 101 返回 -1; 102 } 103 104 //反向查找,返回元素的索引值 105 public intlastIndexOf(Object o) { 106 if(o == null){ 107 for (int i = 大小-1; i >= 0; i--)108 if (elementData[i]==null) 109返回i; 110 } 其他 { 111 for (int i = 大小-1; i >= 0; i--) 112 if (o.equals(elementData[i])) 113返回i; 114 } 115返回-1; 116 } 117 118 // 反向查找(从数组末尾查找到开头),返回元素(o)的索引值 119 public intlastIndexOf(Object o) { 120 if(o == null){ 121 for (int i = 大小-1; i >= 0; i--) 122if(elementData[i]==null) 123返回i; 124 } 其他 { 125 for (int i = 大小-1; i >= 0; i--) 126 if (o.equals(elementData[i])) 127返回i;128} 129返回-1; 130} 131 132 133 // 返回ArrayList的Object数组 134 public Object[] toArray() { 135 return Arrays.copyOf(elementData, size); 136} 137 138 // 返回ArrayList的模板数组。所谓模板数组,就是T可以设置为任意数据类型 139 public T[] toArray(T[] a) { 140 // 如果数组 a 的大小 < ArrayList 中元素的数量; 141 //然后新建一个T[]数组,数组大小为“ArrayList的元素个数”,将“ArrayList”全部复制到新数组中 142 if(a.长度<尺寸) 143 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); 144 145 // 如果数组 a 的大小 >= ArrayList 的元素数量; 146 //然后将ArrayList的所有元素复制到数组a中。 147 System.arraycopy(elementData, 0, a, 0, 大小); 148 if(a.长度 > 尺寸) 149 a[大小] = null;150返回a; 151} 152 153 // 获取索引位置处的元素值 154 公共 E get(int索引) { 155 RangeCheck(索引); 156 157 返回 (E) elementData[index]; 158} 159 160 // 将索引位置的值设置为element 161 public E 集(int 索引,E 元素){ 162 RangeCheck(索引); 163 164 E oldValue = (E) elementData[索引]; 165 elementData[索引] = 元素; 166 返回旧值; 167 } 168 169 // 将 e 添加到 ArrayList 170 公共 布尔值 添加(E e){ 171 确保容量(大小 + 1); // 增量 modCount!! 172 elementData[size++] = e; 173返回真; 174 } 175 176 // 将e添加到ArrayList的指定位置177 public void add(int索引,E元素){ 178 if(索引 > 大小 || 索引 < 0) 179 抛出 新 IndexOutOfBoundsException( 180“索引:”+索引+”,尺寸:“+尺寸); 181 182确保容量(尺寸+1); // 增量 modCount!! 183 System.arraycopy(elementData, 索引, elementData, 索引 + 1, 184尺寸-索引); 185 elementData[索引] = 元素; 186尺寸++; 187} 188 189 //删除ArrayList指定位置的元素 190 公共 E 删除(int 索引){ 191 RangeCheck(索引); 192 193 modCount++; 194 E oldValue = (E) elementData[索引]; 195 196 int numMoved = 大小 - 索引 - 1; 197 if (numMoved > 0)198 System.arraycopy(elementData, 索引+1, elementData, 索引, 199 已移动); 200 elementData[--size] = null; //让gc完成它的工作 201 202 返回旧值; 203} 204 205 //删除ArrayList的指定元素 206 public boolean 删除(对象o){ 207 if(o == null){ 208 for(int索引 = 0;索引 < 大小;索引++) 209 if (elementData[index] == null) { 210 快速删除(索引); 211返回真; 212} 213 } 其他 { 214for(int索引 = 0;索引 < 大小;索引++) 215 if (o.equals(elementData[index])) { 216 快速删除(索引);217返回真; 218} 219} 220返回假; 221} 222 223 224 // 快速删除索引元素 225 私有 void快速删除(int索引){ 226 modCount++; 227 int numMoved = 大小 - 索引 - 1; 228 // 从“index+1”开始,将前一个元素替换为后一个元素。 229 if (numMoved > 0) 230 System.arraycopy(elementData, 索引+1, elementData, 索引, 231 已移动); 232 // 将最后一个元素设置为空 233 elementData[--size] = null; //让gc完成它的工作 234} 235 236 // 删除元素 237 public boolean 删除(对象o){ 238 if(o == null){239 for (int索引 = 0;索引 < 大小;索引++) 240 if(elementData[索引] == null){ 241 fastRemove(index); 242 返回真; 243 } 244 } 否则 { 245 // 方便的ArrayList,如果找到“元素o”,则将其删除并返回true。 246 for(int索引 = 0;索引 < 大小;索引++) 247 if (o.equals(elementData[index])) { 248 fastRemove(index); 249 返回真; 250 } 251 } 252 返回 假; 253 } 254 255 // 清除ArrayList并将所有元素设置为null 256 公共 void 清除(){ 257 modCount++; 258 259 for (int i = 0; i < 大小; i++)260 elementData[i] = null; 261 262尺寸=0; 263 } 101 265 // 从ArrayList中删除关键字 295 公共 boolean addAll(属性 扩展 E> c) { 267 Object[] a = c.toArray(); 268 int numNew = a.length; 269 EnsureCapacity(size + numNew); // 增量 modCount 270 System.arraycopy(a, 0, elementData, size, numNew); 271 大小 += numNew; return 272 return numNew != 0; 273 } 274 275 // 定义一个索引,ArrayList 的 c 属性的钥匙串 276 public boolean addAll(int索引, 延伸 E> c) { 277 if(索引 > 大小 || 索引 < 0) 278 抛出 新 IndexOutOfBoundsException(279“索引:”+索引+“,尺寸:”+尺寸); 280 281 Object[] a = c.toArray(); 282 int numNew = a.length; 283 EnsureCapacity(size + numNew); // 增量 modCount 284 285 int numMoved = 大小 - 索引; 286 if(已移动数 > 0) 287 System.arraycopy(elementData, 索引, elementData, 索引 + numNew, 288 已移动); 289 290 System.arraycopy(a, 0, elementData, 索引, numNew); 291 大小 += numNew; 292 返回 numNew != 0; 293} 294 295 //删除所有从Index到Index之间的元素。 296 受保护 void 删除范围(int fromIndex, int toIndex) { 297 modCount++; 298 int numMoved = 大小 - toIndex;299 System.arraycopy(elementData, toIndex, elementData, fromIndex, 300 已移动); 301 302 // 让 gc 完成它的工作 303 int newSize = 大小 - (toIndex-fromIndex); 304 同时(尺寸!= 新尺寸) 305 elementData[--size] = null; 306 } 307 308 private void RangeCheck(int索引) { 309 if(索引 >= 尺寸) 310 抛出 新 IndexOutOfBoundsException( 311 "索引:"+index+",尺寸:"+尺寸); 312 } 313 314 315 // 克隆函数 316 public 对象克隆() { 317 尝试 { 318 ArrayList v = (ArrayList ) super.clone(); 319 //将当前ArrayList的全部元素复制到v中320 v.elementData = Arrays.copyOf(elementData, size); 321 v.modCount = 0; 322 返回 v; 323 } catch(CloneNotSupportedException e){ 324 // 这不应该发生,因为我们是可克隆的 325 抛出 新InternalError(); 326} 327} 328 329 330 // java.io.Serialized 的写入函数 331 // 将ArrayList的“容量,所有元素值”写入输出流 332 私有 void writeObject(java.io.ObjectOutputStream s) 333 抛出 java.io.IOException{ 334 // 写出元素计数以及任何隐藏的内容 335 int预期ModCount = modCount; 336 s.defaultWriteObject(); 337 338 // 写入“数组的容量” 339 s.writeInt(elementData.length); 340341 // 写入“数组的每个元素” 342 对于 (int i=0; i<大小; i++) 343 s.writeObject(elementData[i]); 344 345 if (modCount !=预期ModCount) { 346 抛出 新ConcurrentModificationException(); 347 } 348 349} 350 351 352 // java.io.Serialized 的读取功能:按照写入方式读取 353 // 先读出ArrayList的“容量”,再读出“所有元素值” 354 私有 void readObject(java.io.ObjectInputStream s) 355 抛出 java.io.IOException, ClassNotFoundException { 356 // 阅读尺寸以及任何隐藏的内容 357 s.defaultReadObject(); 358 359 // 从输入流中读取ArrayList的“容量” 360 int arrayLength = s.readInt(); 361对象[] a = elementData = new对象[arrayLength]; 362363 // 从输入流中读取“所有元素值” 364 对于 (int i=0; i<大小; i++) 365 a[i] = s.readObject(); 366} 367 } 总结:
(01) ArrayList其实就是,通过数组来保存数据。当我们构造ArrayList时;如果使用默认构造函数,ArrayList的默认容量大小为10。
(02) 当ArrayList容量不足以容纳所有元素时,ArrayList会重置容量:新容量 = “(原容量x3)/2 + 1”。
(03) ArrayList的克隆功能是将所有元素克隆到一个数组中。
(04) ArrayList 实现了 java.io.Serialized。写入输出流时,先写入“容量”,然后依次写入“每个元素”;读取输入流时,先读取“容量”,然后依次读取“每个元素”。
第四部分 ArrayList遍历方法
ArrayList支持3种遍历方式
(01) 第一种方式,通过迭代器遍历。也就是通过Iterator进行遍历。
整数值 = null; 迭代器 iter = list.iterator(); while (iter.hasNext()) { 值= (整数)www.sychzs.cn(); }(02) 第二种,随机访问,遍历索引值。
由于ArrayList实现了RandomAccess接口,因此支持通过索引值随机访问元素。整数值 = null; int size = list.size(); for (int i=0; i<大小; i++) { value = (整数)list.get(i); }(03) 第三种,for循环遍历。如下:
整数值 = null; for(整数整数:列表){ 值=整数; }下面是一个例子,比较这三种方法的效率,示例代码(www.sychzs.cn)如下:
查看代码1 导入java.util.*; 2 导入 java.util.concurrent.*; 3 4 /* 5 * @desc ArrayList 遍历方法及效率测试程序。 6 * 7 * @author skywang 8 */ 9 公共 类 ArrayListRandomAccessTest { 1011publicpublicstaticvoidmain(string [] args){ 12 列表列表 = new ArrayList(); 13 对于 (int i=0; i<100000; i++) 14 列表.add(i); 15 //isRandomAccessSupported(列表); 16 迭代器ThroughRandomAccess(list) ; 17 iteratorThroughIterator(list) ; 18 iteratorThroughFor2(list) ; 19 20} 21 22 私有 静态 void支持随机访问(列表列表){ 23 if(列表实例随机访问){ 24 System.out.println("实现随机访问!"); 25 } 其他 { 26 System.out.println("未实现随机访问!"); 27} 28 29} 3031 公共 静态 void迭代器通过随机访问(列表列表){ 32 33 长开始时间; 34 长结束时间; 35 startTime = System.currentTimeMillis(); 36 for (int i=0; i) { 37 list.get(i); 38 } 39 endTime = System.currentTimeMillis(); 40 长间隔=结束时间-开始时间; 41 System.out.println("iteratorThroughRandomAccess:" + 间隔+" ms"); 42 } 43 44 public 静态 void iteratorThroughIterator(List list) ){ 45 46 长开始时间; 47 长结束时间; 48 startTime = System.currentTimeMillis(); 49 for(迭代器 iter = list.iterator(); iter.hasNext(); ) {50 www.sychzs.cn(); 51 } 52 endTime = System.currentTimeMillis(); 53 长间隔=结束时间-开始时间; 54 System.out.println("iteratorThroughIterator:" + 间隔+" ms"); 55 } 56 57 316 public 静态 void iteratorThroughFor2(List list) ){ 59 60 长开始时间; 61 长结束时间; 62 startTime = System.currentTimeMillis(); 63 for(对象 obj:list) 64; 65 endTime = System.currentTimeMillis(); 66 长间隔=结束时间-开始时间; 67 System.out.println("iteratorThroughFor2:" + 间隔+" ms"); 68} 69 }
运行结果:
iteratorThroughRandomAccess:3 毫秒
iteratorThrough迭代器:8 毫秒
iteratorThroughFor2:5 毫秒可以看出,遍历ArrayList时,使用随机访问(即通过索引序号访问)效率最高,而使用迭代器则是效率最低!
第 5 部分 toArray() 异常
当我们在ArrayList中调用toArray()时,可能会遇到“java.lang.ClassCastException”的异常。我们来谈谈发生了什么事。
ArrayList 提供 2 个 toArray() 函数:
Object[] toArray()T[] toArray(T[] 内容) 调用toArray()函数会抛出“java.lang.ClassCastException”异常,但调用toArray(T[]contents)可以正常返回T[]。
toArray() 会抛出异常,因为 toArray() 返回一个 Object[] 数组。将 Object[] 转换为其他类型(例如将 Object[] 转换为 Integer[])将会抛出异常。 “java.lang.ClassCastException”异常,因为 Java 不支持向下转换 。具体可以参考www.sychzs.cn源码介绍部分的toArray()。
这个问题的解决方案是调用T[] toArray(T[]contents) 而不是 Object[] toArray()。 通过以下方式调用toArray(T[]contents)返回T[]。
// toArray(T[]内容)调用方法1public 静态 Integer[] vectorToArray1(ArrayListM 149删除6维ArrayList136v) {; 整数[] newText = new 整数[v.size()]; v. toArray(newText); returnnewText; } } // toArray(T[]内容)defaults public 静态 Integer[] vectorToArray2(ArrayList v) {; Integer[] newText = (Integer[])v.toArray(new Integer[0]); returnnewText; } } // toArray(T[]内容)指定key public 静态 Integer[] vectorToArray3(ArrayList v) {; 整数[] newText = new 整数[v.size()]; Integer[] newStrings =(Integer[])v.toArray(newText); 返回newStrings; } 新安装了一个异常(www.sychzs.cn),ArrayList环境API方法。
查看代码1 导入java.util.*; 2 3 /* 4 * @desc ArrayList 常用 API 测试程序 5 * @author skywang 6 * @email support@www.sychzs.cn 7 */ 8 公共 类 ArrayListTest { 9 10 public 静态 void main(String[] args ) { 11 12 // 创建ArrayList 13 ArrayList 列表 = new ArrayList(); 14 15 // 更改“” 16 list.add("1"); 17 list.add("2"); 18 list.add("3"); 19 list.add("4"); 20 // 将以下元素添加到第一个位置 21 list.add(0, "5"); 22 23 // 获取第一个元素24 System.out.println("第一个元素是: "+ list.get(0)); 25 // 删除“3” 26 list.remove("3"); 27 // 获取ArrayList的大小 28 System.out.println("数组大小=:"+ list.size()); 29 // 判断列表中是否包含“3” 30 System.out.println("ArrayList包含3个是:"+ list.contains(3)); 31 // 将第二个元素设置为 10 32 list.set(1, "10"); 33 34 //通过Iterator遍历ArrayList 35 for(迭代器 iter = list.iterator(); iter.hasNext(); ) { 36 System.out.println("下一个是:"+ www.sychzs.cn()); 37} 38 39 //将ArrayList转换为Array 40 String[] arr = (String[])list.toArray(new String[0]); 41 for(字符串 str:arr)42 System.out.println("str: "+ str); 43 44 //晴空ArrayList 45 list.clear(); 46 //判断ArrayList是或否 47 System.out.println("ArrayList为空:"+ list.isEmpty()); 48 } 49 }运行结果:
第一个元素是:5 数组列表大小=:4 ArrayList包含3个的是: false 下一个是:5 下一个是:10 接下来是:2 下一个是:4 力量:5 力量:10 力量:2 力量:4 ArrayList 为空:true