持有对象

通常,程序总是根据运行时才知道的某些条件去创建对象,在此之前,不会知道所需对象的数量,甚至不知道确切的类型。在 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Iterable<T> {

Iterator<T> iterator();

default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Iterator<E> {

boolean hasNext();

E next();

default void remove() {
throw new UnsupportedOperationException("remove");
}

default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}

添加一组元素

Arrays.asList() 方法接受一个数组或是一个用逗号分隔的元素列表,并将其转换为一个 List 对象。

Arrays.asList() 返回的 list ,底层是一个固定大小的数组,不能调整尺寸。试图使用 add()delete() 方法在这种列表中添加或删除元素,就有可能会引发去改变数组尺寸的尝试,得到异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<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
7
Integer[] 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
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
class F {

static void fill(Collection<String> collection) {
collection.add("R");
collection.add("A");
collection.add("B");
collection.add("B");
System.out.println(collection.toString());
}

static void fill(Map<String, String> map) {
map.put("R", "1");
map.put("A", "2");
map.put("B", "3");
map.put("B", "3");
System.out.println(map.toString());
}
}

F.fill(new ArrayList<String>()); // [R, A, B, B]
F.fill(new LinkedList<String>()); // [R, A, B, B]
F.fill(new HashSet<String>()); // [A, R, B]
F.fill(new TreeSet<String>()); // [A, B, R]
F.fill(new LinkedHashSet<String>()); // [R, A, B]
F.fill(new HashMap<String, String>()); // {A=2, R=1, B=3}
F.fill(new TreeMap<String, String>()); // {A=2, B=3, R=1}
F.fill(new LinkedHashMap<String, String>()); // {R=1, A=2, B=3}

subList()

subList() 方法允许你很容易地从较大的列表中创建出一个片断,但需要注意的是,subList() 所产生的列表的幕后就是初始列表,因此,对所返回的列表的修改都会反映到初始列表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
List<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
15
List<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
11
public 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
14
public 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();
}
}

ListIteratorIterator 的子类型,支持双向移动,还可以替换访问过的最后一个元素。

arrayList.listIterator().previous() 实现:

1
2
3
4
5
6
7
8
9
10
11
public 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
11
public 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
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class K {

static void display(Iterator<String> iterator) {
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
}

static void display(Collection<String> collection) {
for (String s : collection) {
System.out.print(s + " ");
}
System.out.println();
}
}

String[] strings = {"A", "B", "C", "D"};
List<String> list = new ArrayList<>(Arrays.asList(strings));
K.display(list); // A B C D
K.display(list.iterator()); // A B C D

// 当一个类要使用 K.display() 时,必须实现 Collection 或者 Iterator

// 要实现 Collection 的导出类 AbstractCollection
// 就必须要实现 Iterator 接口以及 size() 方法
class A extends AbstractCollection<String> {

private String[] strings = {"A", "B", "C", "D"};

@Override
public Iterator<String> iterator() {
return new Iterator<String>() {

private int index = 0;

@Override
public boolean hasNext() {
return index < strings.length;
}

@Override
public String next() {
return strings[index++];
}
};
}

@Override
public int size() {
return strings.length;
}
}

K.display(new A()); // A B C D

// 当类已经继承其他类时,只能实现 Iterator 接口
class B {
protected String[] strings = {"A", "B", "C", "D"};
}

class BB extends B {
public Iterator<String> iterator() {
return new Iterator<String>() {

private int index = 0;

@Override
public boolean hasNext() {
return index < strings.length;
}

@Override
public String next() {
return strings[index++];
}
};
}
}

K.display(new BB().iterator()); // A B C D

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
40
class 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"};

@Override
public Iterator<String> iterator() {
return new Iterator<String>() {

private int index = 0;

@Override
public boolean hasNext() {
return index < strings.length;
}

@Override
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
62
class 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>() {

@Override
public Iterator<T> iterator() {
return new Iterator<T>() {

private int index = size() - 1;

@Override
public boolean hasNext() {
return index > -1;
}

@Override
public T next() {
return get(index--);
}
};
}
};
}

public Iterable<T> randomized() {

List<T> list = (List<T>) this.clone();

return new Iterable<T>() {

@Override
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
2
3
4
5
6
7
8
9
10
11
12
13
// 原数组顺序也会被打乱
Integer[] integers = {1, 2, 3};
List<Integer> list = Arrays.asList(integers);
Collections.shuffle(list);
System.out.println(Arrays.toString(integers)); // [2, 3, 1]
System.out.println(list); // [2, 3, 1]

// 打乱的是新创建的 ArrayList,与原数组无关
integers = new Integer[]{1, 2, 3};
list = new ArrayList<Integer>(Arrays.asList(integers));
Collections.shuffle(list);
System.out.println(Arrays.toString(integers)); // [1, 2, 3]
System.out.println(list); // [2, 3, 1]