publicinterface List<E> extends Collection<E>
有序集合(也称为序列)。可以对每个集合内的元素由精确的控制(add.set.get.remove)可以通过其整数索引(集合的元素位置)访问元素,并搜索集合中元素的位置IndexOf(),lastIndexOf();
与设置不同,列表通常允许重复的元素。更正式地说,列表通常允许对元素e1和e2进行配对,e1 = e2 (e2),如果它们允许null元素,它们通常允许多个null元素。有人可能希望实现一个禁止重复的列表,在用户试图插入时抛出运行时异常,但我们希望这种用法非常少见,这并不是不可思议的。
列表接口对迭代器的契约、添加、删除、equals和hashCode方法进行了额外的规定,超出了集合接口中指定的规定。为了方便起见,这里还包括了其他继承方法的声明。
List接口提供了四种方法来访问列表元素的位置(索引)。列表(如Java数组)是基于零的。注意,这些操作可能与某些实现(例如LinkedList类)的索引值成比例地执行。因此,如果调用者不知道实现,那么迭代列表中的元素通常更可取。
List接口提供了一个特殊的迭代器,称为ListIterator,它允许元素插入和替换,以及迭代器接口提供的正常操作之外的双向访问。提供了一种方法来获取列表迭代器,它从列表中的指定位置开始。
List接口提供了搜索指定对象的两种方法。从性能的角度来看,这些方法应该谨慎使用。在许多实现中,它们将执行昂贵的线性搜索。
List接口提供了两种方法,可以有效地在列表的任意点插入和删除多个元素。
注意:虽然列表可以将自己包含为元素,但建议非常谨慎:在这样的列表中,equals和hashCode方法不再被定义。
一些列表实现对它们可能包含的元素有限制。例如,有些实现禁止空元素,有些实现对其元素的类型有限制。试图添加一个不合格的元素会抛出一个未检查的异常,通常是NullPointerException或ClassCastException。试图查询不合格元素的存在可能会抛出异常,或者它可能只是返回false;一些实现将展示前者的行为,而一些实现将展示后者。更一般的情况是,尝试对一个不合格的元素进行操作,其完成不会导致在列表中插入一个不合适的元素,可能会抛出异常,或者在实现的选项中可能会成功。在这个接口的规范中,这些异常被标记为“可选”。
这个是JDK1.8API List的实现
修饰符和类型 | 方法和描述 |
boolean | |
void | |
boolean | addAll(Collection<? extends E> c) 将指定集合中的所有元素追加到该列表的末尾,并按照指定的集合的迭代器(可选操作)返回它们。 |
boolean | addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到指定位置的这个列表(可选操作)。 |
void | clear()从这个列表中删除所有元素(可选操作)。 |
boolean | |
boolean | containsAll |
boolean | |
get | |
int | hashCode()返回此列表的哈希代码值。 |
int | |
boolean | isEmpty()如果该列表不包含元素,则返回true。 |
iterator()以适当的顺序返回列表中元素的迭代器。 | |
int | lastIndexOf |
在这个列表中的元素上返回一个列表迭代器(按正确的顺序)。 | |
listIterator | |
remove(int index) 在这个列表中删除指定位置的元素(可选操作)。 | |
boolean | |
boolean | removeAll(Collection<?> c) 从这个列表中删除所有包含在指定集合中的元素(可选操作)。 |
default void | replaceAll 将此列表中的每个元素替换为将运算符应用到该元素的结果。 |
boolean | retainAll(Collection<?> c) 仅保留指定集合中包含的元素(可选操作)。 |
int | size()返回列表中元素的数量。 |
default void | sort 根据指定的Comparator.所引起的顺序对列表进行排序。 |
default Spliterator<E> | spliterator()在此列表中的元素之上创建一个Spliterator。 |
subList 返回该列表中指定的fromIndex(包含)和toIndex(独占)之间的部分的视图。 | |
Object[] | toArray()返回包含该列表中所有元素的数组(从第一个元素到最后一个元素)。 |
<T> T[] | toArray 返回包含该列表中所有元素的数组(从第一个元素到最后一个元素);返回数组的运行时类型是指定数组的运行时类型。 |
List 的抽象实现类 AbstractList :
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
AbstractList继承自 AbstractCollection 类,实现了List接口。整个类设计类似于AbstractCollection,实现了大多数方法,抽象了对于需要根据数据操作的方法。
List 的实现类:
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable,java.io.Serializable
ArrayList 特点:
1、容量不固定,可以动态扩容
2、有序,(基于数组的实现)可重复
3、元素可以Null
4、效率高。速度快,增删慢,
查找操作的时间复杂度是 O(1)
增删操作的时间复杂度是 O(n)
其他操作基本也都是 O(n)
5、线程不安全,
6、当容量不够时,ArrayList 是当前的容量 * 1.5
7、占用空间少,相比LinkedList,不用占用额外的空间维护表结构
MAX_ARRAY_SIZE :数组最大的容量(2147483639)
ArrayList 其实就是基于数组的实现。因此,增删改查操作就变得很容易理解了。
get(index)直接获取数组的底 index 个元素
set(index,object)直接修改数组的第 index 个元素的引用
add(index,object)添加一个元素到index,(这个会牵扯到数组的扩容)
扩容机制,假设我们现在有一个集合 list,里面正好含有10个元素,此时,我们调用 add(object)方法添加一个元素,
public boolean add(E var1) {
//查看是数组长度是否够
this.ensureCapacityInternal(this.size + 1);
this.elementData[this.size++] = var1;
return true;
}
private void ensureCapacityInternal(int var1) {
//检查是否是默认长度为0的数组,如果是则长度设为10
if(this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
var1 = Math.max(10, var1);
}
this.ensureExplicitCapacity(var1);
}
private void ensureExplicitCapacity(int var1) {
++this.modCount;
//当前需要的长度大于数组长度,执行扩容操作
if(var1 - this.elementData.length > 0) {
this.grow(var1);
}
}
private void grow(int var1) {
int var2 = this.elementData.length;
//var3 = var2*1.5;扩容1.5倍
int var3 = var2 + (var2 >> 1);
if(var3 - var1 < 0) {
var3 = var1;
}
//新容量超出最大值
if(var3 - 2147483639 > 0) {
var3 = hugeCapacity(var1);
}
//重新创建了一个1.5倍容量的数组赋值给elementData
this.elementData = Arrays.copyOf(this.elementData, var3);
}
从上面我们可以看到,修改某个角标的值或者查找某个角标的值时,我们可以直接调用数组的操作,效率很高。但是添加和删除则是要操作整个数组的移动,效率稍低。这里我们也可以看到,其实 ArrayList 就是一个针对数组操作的封装类。
LinkedList
刚刚我们看了 ArrayList,ArrayList 的增删操作效率相对较低。因此,Java 给我们设计了另外一种增删效率比较高的集合 LinkedList。LinkedList 继承自AbstractSequentialList。AbstractSequentialList 又继承自AbstractList,并且基于 iterator 实现了默认增删改查操作。再回过头来看 LinkedList,LinkedList 还实现了Deque(双向队列)接口,
LinkedList和ArrayList的区别:
LinkedList和ArrayList的差别主要来自于ArrayList和LinkedList数据结构的不同:
1) 因为ArrayList是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。ArrayList获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
2) 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
3) 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
4) LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Vector
继承了AbstractList 实现了,LIst,RrandomAccess,Cloneable,Serializable。和ArrysList 类似,但区别在于 Vector是同步类(Synchronized),效率相对会比较低。
一般直接使用ArrayList,需要同步的话可以用Collections类的同步List静态方法来转换为同步List。
Vector会在你不需要进行线程安全的时候,强制给你加锁,导致了额外开销,所以基本不用。
Vector 在JDK1.0的时候出生的,而ArrayList是在JDK1.2的时候,才有的。Vector的默认扩容是当前容量*2(也就是两倍),ArryList前面讲过,是1.5倍。Vector 多了一种迭代器 Enumeration。
Enumeration
public interface Enumeration<E> { /** * Tests if this enumeration contains more elements. * * @return <code>true</code> if and only if this enumeration object * contains at least one more element to provide; * <code>false</code> otherwise. */ boolean hasMoreElements(); /** * Returns the next element of this enumeration if this enumeration * object has at least one more element to provide. * * @return the next element of this enumeration. * @exception NoSuchElementException if no more elements exist. */ E nextElement(); }
这个应该是 线程安全的 iterator ?
public class Stack<E> extends Vector<E>Stack 继承自Vector ,也是一个线程安全的集合。
Stack 也是基于数组实现的。
Stack 实现的是栈结构集合
数据结构中,栈也是一种线性的数据结构,遵守 LIFO(后进先出)的操作顺序。
public class Stack<E> extends Vector<E> {
private static final long serialVersionUID = 1224463164541339165L;
public Stack() {
}
//入栈,添加一个元素到数组的最后一个
public E push(E var1) {
this.addElement(var1);
return var1;
}
//出栈,删除数组最后一个元素并返回
public synchronized E pop() {
int var2 = this.size();
Object var1 = this.peek();
this.removeElementAt(var2 - 1);
return var1;
}
//获取最后一个元素,不删除
public synchronized E peek() {
int var1 = this.size();
if(var1 == 0) {
throw new EmptyStackException();
} else {
return this.elementAt(var1 - 1);
}
}
public boolean empty() {
return this.size() == 0;
}
获取栈中的 位置。
public synchronized int search(Object var1) {
int var2 = this.lastIndexOf(var1);
return var2 >= 0?this.size() - var2:-1;
}
}
整个类的实现非常简单,就是继承 Vector,然后添加了 peek、pop、push、search 等方法,然后然添加删除都在最末尾的元素做操作即可。