Java Review (二十七、集合----- List 集合)

@目录


List 集合代表一个元素有序 、可重复的集合,集合中每个元素都有其对应的顺序索引 。 List 集合允许使用重复元素 , 可以通过索引来访问指定位置的集合元素 。 List 集合默认按元素的添加顺序设置元素的索引 。


Java8 改进的List 接口和Listlterator 接口

相比较父接口 Collection,由于 List 是有序集合 , 因此 List 集合里增加 了一些根据索引来操作集合元素的方法。

  • void add(int index, Object element): 将元素 element 插入到 List 集合的 index 处 。
  • boolean addAll(int index, Collection c): 将集合 c 所包含的所有元素都插入到 List 集合的 index处。
  • Object get(int index): 返回集合 index 索引处的元素。
  • int indexOf(Object 0): 返回对象 。 在 List 集合中第 一次出现的位置索引。
  • int lastlndexOf(Object 0): 返 回 对象 。 在 List 集合中最后 一 次出现的位置索引 。
  • Object remove(int index): 删除并返回 index 索引处的元素 。
  • Object set(int index, Object element): 将 index 索引处的元素替换成 e lement 对象,返回被替换的旧元素 。
  • List subList(int fromIndex, int toIndex): 返回从索引 fromlndex (包含)到索引 to Index (不包含)处所有集合元素组成的子集合。

所有 的 List 实现类都可以调用这些方法来操作集合元素。与 Set 集合相比, List 增加了根据索引来插入、替换和删除集合元素 的方法。除此之外 , Java 8 还为 List 接口添加了如下两个默认方法 :

  • void replaceAll(UnaryOperator operator): 根据 operator 指定的计算规则重新设置 List 集合的所有元素。
  • void sort(Comparator c): 根据 Comparator 参数对 List 集合的元素排序 。

下面程序示范了 List 集合的常规用法:

ListTest.java
public class ListTest
{
	public static void main(String[] args)
	{
		List books = new ArrayList();
		// 向books集合中添加三个元素
		books.add(new String("轻量级Java EE企业应用实战"));
		books.add(new String("疯狂Java讲义"));
		books.add(new String("疯狂Android讲义"));
		System.out.println(books);
		// 将新字符串对象插入在第二个位置
		books.add(1 , new String("疯狂Ajax讲义"));
		for (int i = 0 ; i < books.size() ; i++ )
		{
			System.out.println(books.get(i));
		}
		// 删除第三个元素
		books.remove(2);
		System.out.println(books);
		// 判断指定元素在List集合中位置:输出1,表明位于第二位
		System.out.println(books.indexOf(new String("疯狂Ajax讲义"))); //①
		//将第二个元素替换成新的字符串对象
		books.set(1, new String("疯狂Java讲义"));
		System.out.println(books);
		//将books集合的第二个元素(包括)
		//到第三个元素(不包括)截取成子集合
		System.out.println(books.subList(1 , 2));
	}
}

运行结果:

Java Review (二十七、集合----- List 集合)①行代码处,程序试图返回新字符串对象在 List集合中的位置,实际上 List 集合中并未包含该字符串对象 。 因为 List 集合添加宇符串对象时 ,添加的是通过 new 关键宇创建的新字符串对象,①行代码处也是通过 new 关键宇创建的新字符串对象,两个字符串显然不是同一个对象,但 List 的 indexOf 方法依然可以返回 1 。

List 判断两个对象相等只要通过 equals()方法比较返回 true 即可 。


ListTest2.java
class A
{
	public boolean equals(Object obj)
	{
		return true;
	}
}
public class ListTest2
{
	public static void main(String[] args)
	{
		List books = new ArrayList();
		books.add(new String("轻量级Java EE企业应用实战"));
		books.add(new String("疯狂Java讲义"));
		books.add(new String("疯狂Android讲义"));
		System.out.println(books);
		// 删除集合中A对象,将导致第一个元素被删除
		books.remove(new A());     // ①
		System.out.println(books);
		// 删除集合中A对象,再次删除集合中第一个元素
		books.remove(new A());     // ②
		System.out.println(books);
	}
}

运行结果:
Java Review (二十七、集合----- List 集合)执行①行代码时 ,程序试图删除一个 A 对象 , List 将会调用该A对象的equals()方法依次与集合元素进行比较,如果该 equalsO方法 以某个集合元素作为参数时返回 true , List将会删除该元素——A 类重写了 equalsO方法 , 该方法总是返回 true。所 以每次从 List 集合中删除 A 对象时 ,总是删除 List 集合中的第一个元素 。

与 Set 只提供了 一个 iterator()方法不同, List 还额外提供了 一个 listIterator()方法,该方法返回 一个Listlterator 对象, ListIterator 接口继承了Iterator 接口,提供了专门操作 List 的方法 。 ListIterator 接口在Iterator 接口基础上增加了如下方法 。

  • boolean hasPreviousO: 返回该法代器关联的集合是否还有上一个元素 。
  • Object previous(): 返回该迭代器的上一个元素。
  • void add(Object 0): 在指定位置插入一个元素 。

API:java.util.List

API:java.util.ListIterator


ArrayList 和 Vector 实现类


ArrayList结构图

Java Review (二十七、集合----- List 集合)
ArrayList 和 Vector 类都是基于数组实现的 List 类,所以 ArrayList 和 Vector 类封装了一个动态的、允许再分配的 Object[]数组 。 ArrayList 或 Vector 对象使用 initialCapacity 参数来设置该数组的长度, 当向 ArrayList 或 Vector 中添加元素超出了该数组的长度时,它们的 initialCapacity 会自动增加 。

ArrayList构造方法
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 如果传入的初始容量大于0,就新建一个数组存储元素
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 如果传入的初始容量等于0,使用空数组EMPTY_ELEMENTDATA
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        // 如果传入的初始容量小于0,抛出异常
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

对于通常的编程场景,程序员无须关心 ArrayList 或 Vector 的 initialCapacity 。 但如果向ArrayList或 Vector 集合中添加大 量 元素时,可使用ensureCapacity(int minCapacity) 方法一次性地增加initialCapacity 。 这可以减少重分配 的 次数 ,从而提高性能 。

如果开始就知道 ArrayList 或 Vector 集合需要保存多少个元素,则可以在创建它们时就指定initialCapacity 大小 。 如果创建空的 ArrayList 或 Vector 集合时不指定 initialCapacity 参数 ,则 Object[] 数组的长度默认为 10 。

ArrayList属性
/**
 * 默认容量
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 空数组,如果传入的容量为0时使用
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 空数组,传传入容量时使用,添加第一个元素的时候会重新初始为默认容量大小
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存储元素的数组
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * 集合中元素的个数
 */
private int size;

除此之外,ArrayList 和 Vector 还提供了如下两个方法来重新分配 Object[]数组:

  • void ensureCapacity(int minCapacity): 将 ArrayList 或 Vector 集合的 Object[]数组长度增加大于或等于 minCapacity 值。
  • void trimToSize(): 调整 ArrayList 或 Vector 集合 的 Object[]数组长度为 当前元素 的个数 。 调用该方法可减少 ArrayList 或 Vector 集合对象占用 的存储空间 。

ArrayList 和 Vector 在用法上几乎完全相同 ,Vector十分古老,那时候 Java 还没有提供系统的集合框架,所以 Vector 里提供了 一 些方法名很长的方法。

除此之外, ArrayList 和 Vector 的 显著区别是 :
ArrayList 是线程不安全的,当多个线程访问同一个ArrayList 集合时,如果有超过一个线程修改了 ArrayList 集合,则程序必须于动保证该集合的同步性;
Vector 集合则是线程安全的,无须程序保证该集合的同步性。因为 Vector 是线程安全的,所以 Vector的性能比 ArrayList 的性能要低 。

API:java.util.ArrayList

API:java.util.Vector


LinkedList 实现类


LinkedList结构图

Java Review (二十七、集合----- List 集合)
LinkedList 类是 List 接口的实现类 一它是一 个 List 集合 ,可以根据索引来随机访问集合中的元素 。

除此之外, LinkedList 还实现了 Deque 接口,可以被当成双端队列来使用,还可以被当成"栈"来使用 。

下面程序简单示范了 LinkedList 集合的用法:

LinkedListTest.java
public class LinkedListTest
{
	public static void main(String[] args)
	{
		LinkedList books = new LinkedList();
		// 将字符串元素加入队列的尾部
		books.offer("疯狂Java讲义");
		// 将一个字符串元素加入栈的顶部
		books.push("轻量级Java EE企业应用实战");
		// 将字符串元素添加到队列的头部(相当于栈的顶部)
		books.offerFirst("疯狂Android讲义");
		// 以List的方式(按索引访问的方式)来遍历集合元素
		for (int i = 0; i < books.size() ; i++ )
		{
			System.out.println("遍历中:" + books.get(i));
		}
		// 访问、并不删除栈顶的元素
		System.out.println(books.peekFirst());
		// 访问、并不删除队列的最后一个元素
		System.out.println(books.peekLast());
		// 将栈顶的元素弹出“栈”
		System.out.println(books.pop());
		// 下面输出将看到队列中第一个元素被删除
		System.out.println(books);
		// 访问、并删除队列的最后一个元素
		System.out.println(books.pollLast());
		// 下面输出:[轻量级Java EE企业应用实战]
		System.out.println(books);
	}
}

LinkedList 与 ArrayList 的实现机制完全不同:

  • ArrayList 内部以数组的形式来保存集合中的元素 , 因此随机访问集合元素时有较好的性能;
  • 而 LinkedList 内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入、删除元素时性能比较出色(只需改变指针所指的地址即可)。


双向链表

Java Review (二十七、集合----- List 集合)
从链表删除元素

Java Review (二十七、集合----- List 集合)

API:java.util.LinkedList








参考
【1】:《疯狂Java讲义》
【2】:《Java核心技术 卷一》
【3】:Java技术驿站:【死磕 Java 集合】— ArrayList源码分析
【4】:方志朋的专栏:Java基础:Java容器之ArrayList
【5】:Java技术驿站:【死磕 Java 集合】— LinkedList源码分析
【6】:方志朋的专栏:Java基础:Java容器之LinkedList
【7】:廖雪峰的官方网站:使用List

上一篇:Comic Collector 20 for Mac(漫画管理工具)


下一篇:存储过程的理解