java集合框架List
一、List接口概述。
List有个很大的特点就是可以操作角标。
下面开始介绍List接口中相对于Collection接口比较特别的方法。在Collection接口中已经介绍的方法此处就不再赘述。
1.添加
void |
add(int index, E element) 在列表的指定位置插入指定元素(可选操作)。 |
boolean |
addAll(int index, Collection<? extends E> c) 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。 |
这两个方法相对于Collection接口,可以直接在特定的地方插入新的元素或者集合。
2.删除。
E |
remove(int index) 移除列表中指定位置的元素(可选操作)。 |
这个方法相对于Collection接口来说,可以直接删除特定位置上的元素。
3.修改
E |
set(int index, E element) 用指定元素替换列表中指定位置的元素(可选操作)。 |
这个方法可以将指定位置上的元素替换为另外一个元素(对象)。
4.查找
E |
get(int index) 返回列表中指定位置的元素。 |
int |
indexOf(Object o) 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
int |
lastIndexOf(Object o) 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。 |
List<E> |
subList(int fromIndex, int toIndex) 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 |
可以看出,上述List所有特有的方法都是和角标操作有关的,这是和List“有序”特点造成的。
5.List接口的特有方法:listIterator
ListIterator<E> |
listIterator() 返回此列表元素的列表迭代器(按适当顺序)。 |
ListIterator<E> |
listIterator(int index) 返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。 |
案例1:
package list; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Demo1 { public static void main(String[] args){ List list=new ArrayList(); List list1=new ArrayList(); list1.add("hao"); list1.add("guo"); list1.add("zhen"); /** * 直接add元素给后面下标会报这个错 * Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0 at java.util.ArrayList.addAll(ArrayList.java:497) at list.Demo1.main(Demo1.java:14) */ list.add(0,"ni"); list.addAll(1, list1); bL(list); list.remove(0); System.out.println(list.get(0));//list集合元素会自动填充 list.set(0, "guo"); bL(list); System.out.println(list.indexOf("guo")); System.out.println(list.lastIndexOf("guo")); System.out.println(list.subList(0, 2)); bL1(list); } public static void bL(List l){ //Iterator it=l.iterator(); for(Iterator it=l.listIterator();it.hasNext();){ System.out.print(it.next()+" "); } System.out.println(); Iterator itt=l.listIterator(2); while(itt.hasNext()){ System.out.print(itt.next()+" "); } System.out.println(); } public static void bL1(List l){ for(Iterator it=l.listIterator();it.hasNext();){ System.out.print(it.next()+" "); l.add("www"); } } }
1 package p01.BaseCollectionDemo;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6
7 public class ListIteratorDemo {
8
9 public static void main(String[] args)
10 {
11 List al=new ArrayList();
12 al.add("abc1");
13 al.add("abc2");
14 al.add("abc3");
15 al.add("abc4");
16 Demo1(al);
17 }
18
19 private static void Demo1(List al) {
20 for(Iterator it=al.iterator();it.hasNext();)
21 {
22 String str=(String)it.next();
23 System.out.println(str);
24 if(str=="abc1")
25 {
26 al.add("abc5");
27 }
28 }
29 }
30
31
32 }
运行结果出现了异常:

分析:查看API,可以得到API对ConcurrentModificationException异常的描述:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。程序首先是单线程,怎么就对对象作了并发修改呢?真正的原因是,迭代器在遍历之前就已经确定了容器内对象的数量,如果容器内再增加对象,迭代器将无法确定容器内对象的个数。迭代器在迭代对象,而add方法在添加对象,这就造成了对对象的并发修改。API对此类异常的说明是:如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常。例如,如果线程使用快速失败迭代器在 collection 上迭代时直接修改该 collection,则迭代器将抛出此异常。简单来说,不允许迭代的时候集合对元素进行修改。
解决方法:使用ListIterator迭代器。
ListIterator迭代器是List专门的迭代器,对其他集合的迭代并不适用。
package list; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Random; public class Demo2 { public static void main(String[] args){ List<String> al=new ArrayList<String>(); String ma="ma"; al.add("ni"); al.add("hao"); al.add("guo"); al.add("da"); al.add("xia"); /** * 使用ListIterator倒序遍历集合需要先将指针放在后面的位置 */ ListIterator<String> lil=al.listIterator(al.size()); while(lil.hasPrevious()){ Object element=lil.previous(); System.out.print(element+" "); } ListIterator<String> li=al.listIterator(); while(li.hasNext()){ if((String) li.next()=="hao"){ System.out.println(al); li.add(ma); } } for(Iterator it=al.iterator();it.hasNext();){ System.out.print(it.next()+" "); } /** * 使用ListIterator注意: * 是ListIterator的对象使用add方法添加而不是ArrayList对象使用add方法添加 * 并且默认添加在next()当前对象的下一个对象,不会在此次遍历中显现出来 */ } }
1 package p01.BaseCollectionDemo;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.ListIterator;
7
8 public class ListIteratorDemo {
9
10 public static void main(String[] args)
11 {
12 List al=new ArrayList();
13 al.add("abc1");
14 al.add("abc2");
15 al.add("abc3");
16 al.add("abc4");
17 // Demo1(al);
18 Demo2(al);
19 }
20
21 private static void Demo2(List al) {
22 for(ListIterator li=al.listIterator();li.hasNext();)
23 {
24 String str=(String)li.next();
25 if(str.equals("abc1"))
26 {
27 li.add("abc5");
28 }
29 }
30 System.out.println(al);
31
32 }
33
34 private static void Demo1(List al) {
35 for(Iterator it=al.iterator();it.hasNext();)
36 {
37 String str=(String)it.next();
38 System.out.println(str);
39 if(str=="abc1")
40 {
41 al.add("abc5");
42 }
43 }
44 }
45
46
47 }
运行结果:
[abc1, abc5, abc2, abc3, abc4]
解决问题的关键就是使用ListIterator中的add方法添加元素。也就是说无论是迭代还是修改元素,都交给ListIterator来完成。迭代的时候集合对元素进行修改仍然是不允许的。
ListIterator listIterator(int index);方法则是指定了迭代的位置,这个位置可以自行制定,而默认(无参)的情况下是0。
此外,ListIterator还提供了比较有趣的两个方法:
boolean |
hasPrevious() 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。 |
E |
previous() 返回列表中的前一个元素。 |
通过这两个方法可以实现逆向遍历。
6.List接口常用“子类”的特点。
List下有三个类比较常用:Vector、ArrayList、LinkedList。
6.1Vector类。
Vector类自JDK1.0就出现了,它可以说是集合框架的元老级干部。集合框架自JDK2.0出现以后,Vector类就加入了集合框架的队伍并归入了List旗下。Vector类的最大特点就是线程安全,这在整个集合框架中都是少见的。正是因为线程安全,所以执行效率低下,到了现在几乎已经废弃。即使在多线程编程中也不会使用它,而是使用ArrayList加锁代替。Vector内部是一个数组结构,并且是可增长的,步长为100%。Vector增删查询都很慢。
6.2ArrayList类。
ArrayList类是为了替代Vector类而出现的,所以它的功能几乎和Vector类完全相同,但它是非同步的。ArrayList内部也维护了一个变长的数组,增长的步长为50%,查询速度很快。
6.3LinkedList类。
LinkedList是List接口链接列表实现。也就是说我们说的链表。LinkedList最大的特点就是增删速度非常快。
7.LinkedList和ArrayList都有get方法,有什么区别?
两者虽然都是List的子类,但是ArrayList的底层是数组数据结构,在内存中是连续的,所以使用get方法并不用从头开始查找;但是LinkedList底层是链表数据结构,即使使用了get方法,但是仍然需要从头开始查找。
二、Vector类。
1.概述。
VectorJDK1.0就已经出现了,在JDK1.2被收入JAVA集合框架中,作为List旗下的一员。它和新出现的collection不同,它是线程安全的。正因为同步,所以效率低,后来被ArrayList所取代。
2.Vector比较List
Vector中有很多方法都被新出现的方法所取代。
Enumeration<E> |
elements() 返回此向量的组件的枚举。 |
而Enumeration又是什么呢?
Enumeration是一个接口,它的功能和Iterator是重复的,但是它在JDK1.0就已经出现了。它只有两个方法:
boolean |
hasMoreElements() 测试此枚举是否包含更多的元素。 |
E |
nextElement() 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。 |
这两个方法分别对应着Iterator接口中的两个方法:
boolean |
hasNext() 如果仍有元素可以迭代,则返回 true。 |
E |
next() 返回迭代的下一个元素。 |
新的实现应当优先考虑使用Iterator接口而不是Enumeration 接口。
3.代码示例。
package list; import java.util.Enumeration; import java.util.Vector; public class Demo3 { public static void main(String[] args){ Vector<String> lkl=new Vector<String>(); lkl.add("a"); lkl.add("b"); lkl.add("c"); lkl.add("d"); lkl.add("e"); Enumeration<String> e=lkl.elements(); while(e.hasMoreElements()){ String str=e.nextElement(); System.out.print(str+" "); } } }
1 package p02.VectorDemo;
2
3 import java.util.Enumeration;
4 import java.util.Iterator;
5 import java.util.Vector;
6
7 public class VectorDemo
8 {
9 public static void main(String[] args) {
10 Vector v=new Vector();
11 v.add("abc1");
12 v.add("abc2");
13 v.add("abc3");
14 v.add("abc4");
15
16 Demo1(v);
17 Demo2(v);
18 }
19
20 private static void Demo2(Vector v) {
21
22 for(Iterator it=v.iterator();it.hasNext();)
23 {
24 System.out.println(it.next());
25 }
26
27 }
28
29 private static void Demo1(Vector v)
30 {
31 for (Enumeration e = v.elements(); e.hasMoreElements();)
32 {
33 System.out.println(e.nextElement());
34 }
35 }
36
37 }
使用for循环遍历容器是最靠谱的,这是应当注意的地方。
三、LinkedList类。
1.概述。
LinkedList底层是链表数据结构,这就导致了它的内容在内存中并不是连续的。这也使得它增删速度快而查询速度慢。注意,LinkedList中存储的都是对象的地址,即引用,而非对象本身。
2.LinkedList特有的方法。
2.1.添加。
void |
addFirst(E e) 将指定元素插入此列表的开头。 |
void |
addLast(E e) 将指定元素添加到此列表的结尾。 |
2.2.删除
E |
removeFirst() 移除并返回此列表的第一个元素。 |
E |
removeLast() 移除并返回此列表的最后一个元素。 |
E |
pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。 |
E |
pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。 |
后者在JDK1.6之后取代了前者,原因是前者在列表为空的时候抛出了NoSuchElementException异常,而后者则返回Null,这为健壮性的判断创造了更好的条件。
2.3.获取。
E |
getFirst() 返回此列表的第一个元素。 |
E |
getLast() 返回此列表的最后一个元素。 |
E |
peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。 |
E |
peekLast() 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。 |
peekFirst和peekLast方法在JDK1.6之后取代了getFirst方法和getLast方法,原因同上。
四、ArrayList类。
1.概述。
ArrayList和Vector类几乎相同,略。
但需要注意的是,ArrayList中存储的是对象的引用,即地址,而非对象本身。如果ArrayList容器发生了al.add(3);这种情况,但是编译器并没有报错,不能认为是集合中可以存储基本数据类型,事实上在这个过程中发生了两件事:自动装箱以及上转型。这和Object obj=3;有着异曲同工之妙。
2.方法。
略。
注意,虽然ArrayList略掉很多,但并不是说它不重要,只是因为方法中和List接口中的差不多,所以就不做介绍了。实际上在开发中最常使用的类就是ArrayList。


浙公网安备 33010602011771号