通常,程序总是根据运行时才知道的某些条件去创建对象,在此之前,不会知道所需对象的数量,甚至不知道确切的类型。在 Java 中,通过容器来解决这个普遍的编程问题。
Collections 与 Collection
Collections
是一个实用类,Collection
是一个基本接口。1
public class Collections {}
1 | public interface Collection<E> extends Iterable<E> {} |
Iterable 与 Iterator
Iterable
拥有产生 Iterator
的方法,但是并不拥有迭代状态,比如,当前元素。
Iterator
拥有迭代状态,主要体现在 hasNext()
,next
这些方法上。
1 | public interface Iterable<T> { |
1 | public interface Iterator<E> { |
添加一组元素
Arrays.asList()
方法接受一个数组或是一个用逗号分隔的元素列表,并将其转换为一个 List
对象。
Arrays.asList()
返回的 list
,底层是一个固定大小的数组,不能调整尺寸。试图使用 add()
或 delete()
方法在这种列表中添加或删除元素,就有可能会引发去改变数组尺寸的尝试,得到异常。1
2
3
4
5
6
7
8
9
10
11
12
13
14List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.add(6); // java.lang.UnsupportedOperationException
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(list.toString()); // [1, 2, 3, 4, 5]
list.set(2, 0);
System.out.println(list.toString()); // [1, 2, 0, 4, 5]
List<Integer> list = Arrays.<Integer>asList(1, 2, 3, 4, 5); // 显式类型参数说明
List<Integer> integerList = new ArrayList<Integer>(list);
integerList.add(6);
System.out.println(integerList.toString()); // [1, 2, 3, 4, 5, 6]
Collections.addAll()
方法接受一个 Collection
对象,以及一个数组或是一个用逗号分割的列表,将元素添加到 Collection
对象中。1
2
3
4
5
6
7Integer[] integers = {6, 7};
Collection<Integer> collection = new ArrayList<Integer>();
collection.addAll(Arrays.asList(1, 2, 3));
Collections.addAll(collection, 4, 5);
Collections.addAll(collection, integers);
System.out.println(collection.toString()); // [1, 2, 3, 4, 5, 6, 7]
容器的打印
1 | class F { |
subList()
subList()
方法允许你很容易地从较大的列表中创建出一个片断,但需要注意的是,subList()
所产生的列表的幕后就是初始列表,因此,对所返回的列表的修改都会反映到初始列表中。1
2
3
4
5
6
7
8
9
10
11
12
13List<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> subList = list.subList(1, 3);
System.out.println(subList.toString()); // [2, 3]
subList.set(0, 10);
System.out.println(subList.toString()); // [10, 3]
System.out.println(list.toString()); // [1, 10, 3, 4, 5]
subList.add(100);
System.out.println(subList.toString()); // [10, 3, 100]
System.out.println(list.toString()); // [1, 10, 3, 100, 4, 5]
System.out.println(list.containsAll(subList)); // true
由于 subList()
所产生的列表拥有原始列表的引用,所以在这期间,原始列表所占用的空间并没有被垃圾回收器回收。当原始列表很大时,容易造成内存泄漏。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15List<List<Integer>> lists = new ArrayList<List<Integer>>();
try {
while (true) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 1000000; i++) {
list.add(i);
}
lists.add(list.subList(0, 1));
}
} finally {
System.out.println(lists.size()); // java.lang.OutOfMemoryError: Java heap space
}
迭代器
迭代器是一个对象,它的工作是遍历并选择序列中的对象。
arrayList.iterator().next()
实现:1
2
3
4
5
6
7
8
9
10
11public E next() {
checkForComodification();
int i = cursor; // cursor 初始值为 0
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; // 向后移动,cursor 变为 1
return (E) elementData[lastRet = i]; // 返回 elementData[0]
}
arrayList.iterator().remove()
实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14public void remove() {
if (lastRet < 0) // lastRet 初始值为 -1,经过 next() 之后,lastRet 得到值
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); // 根据 lastRet 删除
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
ListIterator
是 Iterator
的子类型,支持双向移动,还可以替换访问过的最后一个元素。
arrayList.listIterator().previous()
实现:1
2
3
4
5
6
7
8
9
10
11public E previous() {
checkForComodification();
int i = cursor - 1; // 向前移动
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i]; // 与 next() 不同,cursor 与 lastRet 同值
}
arrayList.listIterator().set()
实现:1
2
3
4
5
6
7
8
9
10
11public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e); // 根据 lastRet 替换
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
Collection 与 Iterator
1 | class K { |
forEach 与 迭代器
Collection
之所以能够使用 forEach
,是因为 Collection
继承了 Iterable
。
对于 forEach
,可以用于数组或其他任何实现了 Iterable
的类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40class K {
static <T> void display(Iterable<T> iterable) {
for (T t : iterable) {
System.out.print(t + " ");
}
System.out.println();
}
}
class A implements Iterable<String> {
private String[] strings = {"A", "B", "C"};
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
public boolean hasNext() {
return index < strings.length;
}
public String next() {
return strings[index++];
}
};
}
}
K.display(new A()); // A B C
// 数组无法直接转换为 Iterable
// List<E> extends Collection<E>
// Collection<E> extends Iterable<E>
Integer[] integers = {1, 2, 3};
K.display(Arrays.asList(integers)); // 1 2 3
在 forEach
里面,如果想要拥有多种不同的遍历方法(例如从后向前,或者随机遍历),可以采用 适配器方法
。即,创建不同的方法,返回不同的 Iterable
对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62class K {
static <T> void display(Iterable<T> iterable) {
for (T t : iterable) {
System.out.print(t + " ");
}
System.out.println();
}
}
class A<T> extends ArrayList<T> {
public A(Collection<T> collection) {
super(collection);
}
public Iterable<T> reversed() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = size() - 1;
public boolean hasNext() {
return index > -1;
}
public T next() {
return get(index--);
}
};
}
};
}
public Iterable<T> randomized() {
List<T> list = (List<T>) this.clone();
return new Iterable<T>() {
public Iterator<T> iterator() {
Collections.shuffle(list);
return list.iterator();
}
};
}
}
Integer[] integers = {1, 2, 3};
A a = new A(Arrays.asList(integers));
// A 继承的 ArrayList 有默认的 Iterable
K.display(a); // 1 2 3
K.display(a.reversed()); // 3 2 1
K.display(a.randomized()); // 2 3 1
Collections.shuffle()
1 | // 原数组顺序也会被打乱 |