Java基础(44):ArrayList使用详解

1、什么是ArrayList 
ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处: 
   a.动态的增加和减少元素 
   b.实现了ICollection和IList接口 
   c.灵活的设置数组的大小

2、如何使用ArrayList 
最简单的例子: 
ArrayList List = new ArrayList(); 
for( int i=0;i <10;i++ ) //给数组增加10个Int元素 
List.Add(i); 
//..程序做一些处理 
List.RemoveAt(5);//将第6个元素移除 
for( int i=0;i <3;i++ ) //再增加3个元素 
List.Add(i+20); 
Int32[] values = (Int32[])List.ToArray(typeof(Int32));//返回ArrayList包含的数组

这是一个简单的例子,虽然没有包含ArrayList所有的方法,但是可以反映出ArrayList最常用的用法

3、ArrayList重要的方法和属性 
1)构造器 
ArrayList提供了三个构造器: 
public ArrayList(); 
默认的构造器,将会以默认(16)的大小来初始化内部的数组 
public ArrayList(ICollection); 
用一个ICollection对象来构造,并将该集合的元素添加到ArrayList 
public ArrayList(int); 
用指定的大小来初始化内部的数组

2)IsSynchronized属性和ArrayList.Synchronized方法 
IsSynchronized属性指示当前的ArrayList实例是否支持线程同步,而ArrayList.Synchronized静态方法则会返回一个ArrayList的线程同步的封装。 
如果使用非线程同步的实例,那么在多线程访问的时候,需要自己手动调用lock来保持线程同步,例如: 
ArrayList list = new ArrayList(); 
//... 
lock( list.SyncRoot ) //当ArrayList为非线程包装的时候,SyncRoot属性其实就是它自己,但是为了满足ICollection的SyncRoot定义,这里还是使用SyncRoot来保持源代码的规范性 

list.Add( “Add a Item” ); 
}

如果使用ArrayList.Synchronized方法返回的实例,那么就不用考虑线程同步的问题,这个实例本身就是线程安全的,实际上ArrayList内部实现了一个保证线程同步的内部类,ArrayList.Synchronized返回的就是这个类的实例,它里面的每个属性都是用了lock关键字来保证线程同步。

3)Count属性和Capacity属性 
Count属性是目前ArrayList包含的元素的数量,这个属性是只读的。 
Capacity属性是目前ArrayList能够包含的最大数量,可以手动的设置这个属性,但是当设置为小于Count值的时候会引发一个异常。

4)Add、AddRange、Remove、RemoveAt、RemoveRange、Insert、InsertRange 
这几个方法比较类似 
Add方法用于添加一个元素到当前列表的末尾 
AddRange方法用于添加一批元素到当前列表的末尾 
Remove方法用于删除一个元素,通过元素本身的引用来删除 
RemoveAt方法用于删除一个元素,通过索引值来删除 
RemoveRange用于删除一批元素,通过指定开始的索引和删除的数量来删除 
Insert用于添加一个元素到指定位置,列表后面的元素依次往后移动 
InsertRange用于从指定位置开始添加一批元素,列表后面的元素依次往后移动

另外,还有几个类似的方法: 
Clear方法用于清除现有所有的元素 
Contains方法用来查找某个对象在不在列表之中

其他的我就不一一累赘了,大家可以查看MSDN,上面讲的更仔细 
5)TrimSize方法 
这个方法用于将ArrayList固定到实际元素的大小,当动态数组元素确定不在添加的时候,可以调用这个方法来释放空余的内存。 
6)ToArray方法 
这个方法把ArrayList的元素Copy到一个新的数组中。 
4、ArrayList与数组转换 
例1: 
ArrayList List = new ArrayList(); 
List.Add(1); 
List.Add(2); 
List.Add(3);

Int32[] values = (Int32[])List.ToArray(typeof(Int32));

例2: 
ArrayList List = new ArrayList(); 
List.Add(1); 
List.Add(2); 
List.Add(3);

Int32[] values = new Int32[List.Count]; 
List.CopyTo(values);

上面介绍了两种从ArrayList转换到数组的方法

例3: 
ArrayList List = new ArrayList(); 
List.Add( “string” ); 
List.Add( 1 ); 
//往数组中添加不同类型的元素

object[] values = List.ToArray(typeof(object)); //正确 
string[] values = (string[])List.ToArray(typeof(string)); //错误

和数组不一样,因为可以转换为Object数组,所以往ArrayList里面添加不同类型的元素是不会出错的,但是当调用ArrayList方法的时候,要么传递所有元素都可以正确转型的类型或者Object类型,否则将会抛出无法转型的异常。

5、ArrayList最佳使用建议 

这一节我们来讨论ArrayList与数组的差别,以及ArrayList的效率问题 
1)ArrayList是Array的复杂版本 
ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。 
2)内部的Object类型的影响 
对于一般的引用类型来说,这部分的影响不是很大,但是对于值类型来说,往ArrayList里面添加和修改元素,都会引起装箱和拆箱的操作,频繁的操作可能会影响一部分效率。 
但是恰恰对于大多数人,多数的应用都是使用值类型的数组。 
消除这个影响是没有办法的,除非你不用它,否则就要承担一部分的效率损失,不过这部分的损失不会很大。 
3)数组扩容 
这是对ArrayList效率影响比较大的一个因素。 
每当执行Add、AddRange、Insert、InsertRange等添加元素的方法,都会检查内部数组的容量是否不够了,如果是,它就会以当前容量的两倍来重新构建一个数组,将旧元素Copy到新数组中,然后丢弃旧数组,在这个临界点的扩容操作,应该来说是比较影响效率的。 
例1:比如,一个可能有200个元素的数据动态添加到一个以默认16个元素大小创建的ArrayList中,将会经过: 
16*2*2*2*2 = 256 
四次的扩容才会满足最终的要求,那么如果一开始就以: 
ArrayList List = new ArrayList( 210 ); 
的方式创建ArrayList,不仅会减少4次数组创建和Copy的操作,还会减少内存使用。

例2:预计有30个元素而创建了一个ArrayList: 
ArrayList List = new ArrayList(30); 
在执行过程中,加入了31个元素,那么数组会扩充到60个元素的大小,而这时候不会有新的元素再增加进来,而且有没有调用TrimSize方法,那么就有1次扩容的操作,并且浪费了29个元素大小的空间。如果这时候,用: 
ArrayList List = new ArrayList(40); 
那么一切都解决了。 
所以说,正确的预估可能的元素,并且在适当的时候调用TrimSize方法是提高ArrayList使用效率的重要途径。 
4)频繁的调用IndexOf、Contains等方法(Sort、BinarySearch等方法经过优化,不在此列)引起的效率损失

首先,我们要明确一点,ArrayList是动态数组,它不包括通过Key或者Value快速访问的算法,所以实际上调用IndexOf、Contains等方法是执行的简单的循环来查找元素,所以频繁的调用此类方法并不比你自己写循环并且稍作优化来的快,如果有这方面的要求,建议使用Hashtable或SortedList等键值对的集合。 
ArrayList al=new ArrayList();

al.Add("How"); 
al.Add("are"); 
al.Add("you!");

al.Add(100); 
al.Add(200); 
al.Add(300);

al.Add(1.2); 
al.Add(22.8);

使用ArrayList类
ArrayList类实现了List接口,由ArrayList类实现的List集合采用数组结构保存对象。数组结构的优点是便于对集合进行快速的随机访问,如果经常需要根据索引位置访问集合中的对象,使用由ArrayList类实现的List集合的效率较好。数组结构的缺点是向指定索引位置插入对象和删除指定索引位置对象的速度较慢,如果经常需要向List集合的指定索引位置插入对象,或者是删除List集合的指定索引位置的对象,使用由ArrayList类实现的List集合的效率则较低,并且插入或删除对象的索引位置越小效率越低,原因是当向指定的索引位置插入对象时,会同时将指定索引位置及之后的所有对象相应的向后移动一位,如图1所示。当删除指定索引位置的对象时,会同时将指定索引位置之后的所有对象相应的向前移动一位,如图2所示。如果在指定的索引位置之后有大量的对象,将严重影响对集合的操作效率。

就是因为用ArrayList类实现的List集合在插入和删除对象时存在这样的缺点,在编写例程06时才没有利用ArrayList类实例化List集合,下面看一个模仿经常需要随机访问集合中对象的例子。
在编写该例子时,用到了java.lang.Math类的random()方法,通过该方法可以得到一个小于10的double型随机数,将该随机数乘以5后再强制转换成整数,将得到一个0到4的整数,并随机访问由ArrayList类实现的List集合中该索引位置的对象,具体代码如下:
src\com\mwq\TestCollection.java关键代码:
public static void main(String[] args) {
String a = "A", b = "B", c = "C", d = "D", e = "E";
List<String> list = new ArrayList<String>();
list.add(a);      // 索引位置为 0
list.add(b);      // 索引位置为 1
list.add(c);      // 索引位置为 2
list.add(d);      // 索引位置为 3
list.add(e);      // 索引位置为 4
System.out.println(list.get((int) (Math.random() * 5)));     // 模拟随机访问集合中的对象
}

我实际中的练习例子:

 1 package code;
 2 import java.util.ArrayList;
 3 import java.util.Iterator;
 4 public class SimpleTest {
 5
 6
 7  public static void main(String []args){
 8
 9   ArrayList list1 = new ArrayList();
10   list1.add("one");
11   list1.add("two");
12   list1.add("three");
13   list1.add("four");
14   list1.add("five");
15   list1.add(0,"zero");
16   System.out.println("<--list1中共有>" + list1.size()+ "个元素");
17   System.out.println("<--list1中的内容:" + list1 + "-->");
18
19   ArrayList list2 = new ArrayList();
20   list2.add("Begin");
21   list2.addAll(list1);
22   list2.add("End");
23   System.out.println("<--list2中共有>" + list2.size()+ "个元素");
24   System.out.println("<--list2中的内容:" + list2 + "-->");
25
26   ArrayList list3 =  new ArrayList();
27   list3.removeAll(list1);
28   System.out.println("<--list3中是否存在one: "+ (list3.contains("one")? "是":"否")+ "-->");
29
30   list3.add(0,"same element");
31   list3.add(1,"same element");
32   System.out.println("<--list3中共有>" + list3.size()+ "个元素");
33   System.out.println("<--list3中的内容:" + list3 + "-->");
34   System.out.println("<--list3中第一次出现same element的索引是" + list3.indexOf("same element") + "-->");
35   System.out.println("<--list3中最后一次出现same element的索引是" + list3.lastIndexOf("same element") + "-->");
36
37
38   System.out.println("<--使用Iterator接口访问list3->");
39   Iterator it = list3.iterator();
40   while(it.hasNext()){
41    String str = (String)it.next();
42    System.out.println("<--list3中的元素:" + list3 + "-->");
43   }
44
45   System.out.println("<--将list3中的same element修改为another element-->");
46   list3.set(0,"another element");
47   list3.set(1,"another element");
48      System.out.println("<--将list3转为数组-->");
49     // Object []  array =(Object[]) list3.toArray(new   Object[list3.size()] );
50      Object [] array = list3.toArray();
51      for(int i = 0; i < array.length ; i ++){
52       String str = (String)array[i];
53       System.out.println("array[" + i + "] = "+ str);
54      }
55
56      System.out.println("<---清空list3->");
57      list3.clear();
58      System.out.println("<--list3中是否为空: " + (list3.isEmpty()?"是":"否") + "-->");
59      System.out.println("<--list3中共有>" + list3.size()+ "个元素");
60
61   //System.out.println("hello world!");
62  }
63 } 
时间: 2024-10-03 09:55:17

Java基础(44):ArrayList使用详解的相关文章

java基础之HTTP协议详解

关于本文,是听了某个老师的课程之后倍感自己对HTTP了解不够深入,因此决定写此博文. 首先,可以参考此文:http://blog.csdn.net/gueter/article/details/1524447 第一部分:浏览器与服务器通信过程: 第二部分:HTTP请求介绍: 第三部分:HTTP协议响应 java基础之HTTP协议详解

Java基础之hashCode方法详解

想要明白hashCode的作用,必须要先知道java中的集合.(不明白的请看Java基础之集合框架详解(二)List篇和Java基础之集合框架详解(三)Set篇) Java中的Collection集合有两类,一类是List,另一类是Set,前者集合内的元素是有序的,元素可以重复:后者元素无序且元素不可重复.而我们通常使用Object.equals方法来判断两个元素是否重复.即当我们想查找一个元素中是否包含某个对象时,就是逐一取出每个元素与要找的元素进行比较,当发现某个元素与要查找的对象进行equ

JAVA基础汇总及例题详解

java语言的一个核心: jdk, java development kits---面向开发人员 jre, java Runtime Environment---服务器上 java虚拟机---(以字节码为指令的CPU)---*.class java编译器-->字节码-->类加载器进行验证-->虚拟机运行 垃圾回收机制 public class ...{ int a = 1; } c/c++垃圾回收由程序员去运行 java编程语言(一门纯面向对象)的特点: 1, 面向对象  封装  继承

Java基础(55):Exception类详解(转)

Java中的异常 Exception java.lang.Exception类是Java中所有异常的直接或间接父类.即Exception类是所有异常的根类. 比如程序: 1 public class ExceptionTest 2 { 3 public static void main(String[] args) 4 { 5 int a = 3; 6 int b = 0; 7 int c = a / b; 8 System.out.println(c); 9 } 10 } 编译通过,执行时结果

Java基础之集合框架详解(一)

集合框架的相关定义 集合:即存储对象的容器. 框架:即类库的集合. 集合框架:即用来表示和操作集合的统一框架,由相关的接口.实现类和一些帮助程序员完成编程的算法. 集合的作用: 在类的内部,对数据进行组织 简单而快速的搜索大数量的条目 有的集合接口,提供了一系列排列有序的元素,并且可以在序列中间快速的插入或者删除有关元素 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型 集合与数组的区别: 集合长度是可变的,数组长度是固定不变的 集合中

Java基础之开关语句详解

switch 语句是单条件多分支的开关语句,它的一般格式定义如下(其中break语句是可选的): switch(表达式) { case 常量值: 若干个语句 break; case  常量值: 若干个语句 break; ... case  常量值n: 若干个语句 break; default: 若干语句 } switch语句中的"表达式"和"常量值"的值必须为byte.short.int.char型或者枚举类型,而且互不相同. import java.util.Sc

Java基础之集合框架详解(三)Set篇

相关定义 作为Collection接口的重要子接口,Set接口是一个不包含重复元素,且元素排列无序的集合,也被称为集. 注意:不包含重复元素的含义,更确切的讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素. Set接口的方法和Collection接口的方法大体相同,也就是说Set接口不像List接口那样可以通过索引或去元素,只能通过Iterator()方法获取迭代器进行迭代. Set中的add方法不同于Collection接口,有一个

Java基础之I/O详解(二)类库设计模式

Java类库的设计者通过创建大量的类来解决I/O系统设计的难题.一开始,可能会对Java I/O系统提供了如此多的类感到不知所措(具有讽刺意味的是,Java I/O设计的初衷是为了避免过多的类) --–<Thinking in Java> 介于Java I/O类库中数目众多的类,我们有必要清楚这个类库是如何设计的. 分类 Java1.0版本以前,Java中与输入有关的所有类都应该从InputStream这个抽象类继承,而与输出类有关的所有类都应当从OutputStream这个抽象类继承.但是,

Java基础与案例开发详解の常用类的介绍

String类 String类的常用构造方法 String s1 = new String(); String s2 = new String("hello"); String s3 = new String(char tmp[]); String s4 = new String(char[] value,int offset,int count) 除了以上方法构造String对象之外,Java编译器会自动为每一个字符串常量生成一个String 类的实例,因此字符串常量String有一