蓝瘦!香菇! 连着加班几天,醉了。学学 List 放松下!
The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list.
List
和 数组一样,都是从 0 开始,我们可以根据元素在 list 中的位置进行操作,比如说 get
, set
, add
, addAll
, remove
;indexOf
, lastIndexOf
;subList
方法对 list 进行任意范围的操作。remove(Object)
add(E)
, addAll(Collection)
用于把新元素添加到 list 的尾部,下面这段语句使得 list3 等于 list1 与 list2 组合起来的内容:
List list3 = new ArrayList(list1);
list3.addAll(list2);
注意:上述使用了 ArrayList
的转换构造函数:
public ArrayList(Collection
Object
的 equlas
() 方法默认和 ==
一样,比较的是地址是否相等。public boolean equals(Object o) {
return this == o;
}
因此和 Set,Map 一样,List 中如果想要根据两个对象的内容而不是地址比较是否相等时,需要重写 equals()
和 hashCode()
方法。 remove()
, contains()
, indexOf()
等等方法都需要依赖它们:
@Override
public boolean contains(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
//需要重载 Object 默认的 equals
if (object.equals(a[i])) {
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
return true;
}
}
}
return false;
}
@Override
public int indexOf(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
return i;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
return i;
}
}
}
return -1;
}
两个 List 对象的所有位置上元素都一样才能相等。
基础的位置访问操作方法有:
get
, set
, add
, remove
indexOf
, lastIndexOf
addAll(int,Collection)
public static void swap(List a, int i, int j) {
E tmp = a.get(i);
a.set(i, a.get(j));
a.set(j, tmp);
}
不同的是它是多态的,允许任何 List 的子类使用。 Collections 中的 shuffle 就有用到和下面这种相似的交换方法:
public static void shuffle(List list, Random rnd) {
for (int i = list.size(); i > 1; i--)
swap(list, i - 1, rnd.nextInt(i));
}
这种算法使用指定的随机算法,从后往前重复的进行交换。和一些其他底层 shuffle 算法不同,这个算法更加公平(随机方法够随机的话,所有元素的被抽到的概率一样),同时够快(只要 list.size() -1
)次交换。
List.subList(int fromIndex, int toIndex) 方法返回 List 在 fromIndex 与 toIndex 范围内的子集。注意是左闭右开,[fromIndex,toIndex)。
注意! List.subList
方法并没有像我们想的那样:创建一个新的 List,然后把旧 List 的指定范围子元素拷贝进新 List,根!本!不!是!
subList 返回的扔是 List 原来的引用,只不过把开始位置 offset 和 size 改了下,见 List.subList() 在 AbstractList 抽象类中的实现:
public List subList(int start, int end) {
if (start >= 0 && end <= size()) {
if (start <= end) {
if (this instanceof RandomAccess) {
return new SubAbstractListRandomAccess(this, start, end);
}
return new SubAbstractList(this, start, end);
}
throw new IllegalArgumentException();
}
throw new IndexOutOfBoundsException();
}
SubAbstractListRandomAccess 最终也是继承 SubAbstractList,直接看 SubAbstractList:
SubAbstractList(AbstractList list, int start, int end) {
fullList = list;
modCount = fullList.modCount;
offset = start;
size = end - start;
}
可以看到,的确是保持原来的引用。
由于 subList 持有 List 同一个引用,所以对 subList 进行的操作也会影响到原有 List,举个栗子:
你猜运行结果是什么?
验证了上述重点。
shixinList.subList(fromIndex, toIndex).clear();
还可以查找某元素在局部范围内的位置:
int i = list.subList(fromIndex, toIndex).indexOf(o);
int j = list.subList(fromIndex, toIndex).lastIndexOf(o);
public boolean add(E object) {
Object[] a = array;
int s = size;
//当放满时,扩容
if (s == a.length) {
//MIN_CAPACITY_INCREMENT 为常量,12
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}
public boolean add(E object) {
return addLastImpl(object);
}
private boolean addLastImpl(E object) {
Link oldLast = voidLink.previous;
Link newLink = new Link(object, oldLast, voidLink);
voidLink.previous = newLink;
oldLast.next = newLink;
size++;
modCount++;
return true;
}
transient Link voidLink;
private static final class Link {
ET data;
Link previous, next;
Link(ET o, Link p, Link n) {
data = o;
previous = p;
next = n;
}
}
public Object[] toArray() {
int s = size;
Object[] result = new Object[s];
//这里的 array 就是 ArrayList 的底层实现,直接拷贝
//System.arraycopy 是底层方法,效率很高
System.arraycopy(array, 0, result, 0, s);
return result;
}
public T[] toArray(T[] contents) {
int s = size;
//先判断参数能不能放下这么多元素
if (contents.length < s) {
//放不下就创建个新数组
@SuppressWarnings("unchecked") T[] newArray
= (T[]) Array.newInstance(contents.getClass().getComponentType(), s);
contents = newArray;
}
System.arraycopy(this.array, 0, contents, 0, s);
if (contents.length > s) {
contents[s] = null;
}
return contents;
}
public Object[] toArray() {
int index = 0;
Object[] contents = new Object[size];
Link link = voidLink.next;
while (link != voidLink) {
//挨个赋值,效率不如 ArrayList
contents[index++] = link.data;
link = link.next;
}
return contents;
}
@Override
@SuppressWarnings("unchecked")
public T[] toArray(T[] contents) {
int index = 0;
if (size > contents.length) {
Class ct = contents.getClass().getComponentType();
contents = (T[]) Array.newInstance(ct, size);
}
Link link = voidLink.next;
while (link != voidLink) {
//还是比 ArrayList 慢
contents[index++] = (T) link.data;
link = link.next;
}
if (index < contents.length) {
contents[index] = null;
}
return contents;
}
asList
:@SafeVarargs
public static List asList(T... array) {
return new ArrayList(array);
}
使用的是 Arrays 内部创建的 ArrayList 的转换构造函数:
private final E[] a;
ArrayList(E[] storage) {
if (storage == null) {
throw new NullPointerException("storage == null");
}
//直接复制
a = storage;
}
public int indexOf(E e) {
for (ListIterator it = listIterator(); it.hasNext(); )
if (e == null ? it.next() == null : e.equals(it.next()))
return it.previousIndex();
// Element not found
return -1;
}
public static void replace(List list, E val, E newVal) {
for (ListIterator it = list.listIterator(); it.hasNext(); )
if (val == null ? it.next() == null : val.equals(it.next()))
it.set(newVal);
}
关联:
Collection, ListIterator, Collections
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/List.html
https://docs.oracle.com/javase/tutorial/collections/interfaces/list.html
http://blog.csdn.net/mazhimazh/article/details/17759579#comments
http://www.blogjava.net/flysky19/articles/92775.html
欢迎扫描关注我的微信公众号,不定时更新我的成长及思考文章~