- Arrays.asList( )方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个List对象。但是该List对象的物理实现依然是数组,因此不能对其进行add( )或者delete( )操作,因为数组尺寸不可变。同样,所有对list对象的操作都是在原来数组上进行的。例如,使用Collections.sort( )方法对list对象进行排序,会对原来的数组造成影响:
Integer[] array = {1, 3, 5, 2};
List<Integer> list1 = Arrays.asList(array);
Collections.sort(list1);
System.out.println("list1 after soft: " + list1); // list1 after soft: [1, 2, 3, 5]
System.out.println("array: " + Arrays.toString(array)); // array: [1, 2, 3, 5]
使用List包装后就不会出现上述问题:
Integer[] array = {1, 3, 5, 2};
List<Integer> list1 = new ArrayList<>(Arrays.asList(array));
Collections.sort(list1);
System.out.println("list1 after soft: " + list1); // list1 after soft: [1, 2, 3, 5]
System.out.println("array: " + Arrays.toString(array)); // array: [1, 3, 5, 2]
但是使用一组元素初始化一个Collection的首选方式一般是:首先构建一个空Collection,然后调用Collections.addAll( )方法将数组中所有元素加入。
Integer[] array = {1, 3, 5, 2};
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, array);
Collections.addAll(list2, 2, 7, 9, 10);
System.out.println("list2: " + list2); // list2: [1, 3, 5, 2, 2, 7, 9, 10]
- 数组的打印必须调用Arrays.toString( )方法,但是容器的打印无需任何帮助,直接打印就可产生可读性很好的结果。
- List中有一个subList( )方法,可以从较大的List中截取一个片断:
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 2, 7, 9, 10);
System.out.println(list2.subList(1, 4)); // [7, 9, 10]
System.out.println("list2: "+ list2); // list2: [2, 7, 9, 10]
retainAll( )方法由一个List1调用,另一个List2作为参数传入,最终在List1中保留两者的交集,其余元素删除。特别注意判断元素是否相同要依靠equals方法,如果List储存的是我们自己创建的对象,同时又未重写equals方法,那么比较的是两个对象的内存地址。
Integer[] array = {1, 3, 5, 2};
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list1, array);
Collections.addAll(list2, 2, 7, 9, 10);
System.out.println("list1: " + list1);
System.out.println("list2: " + list2);
list2.retainAll(list1);
System.out.println("list2: " + list2);
/*
list1: [1, 3, 5, 2]
list2: [2, 7, 9, 10]
list2: [2]
*/
List中存在一个重载方法addAll( )可以在指定索引处插入Collection,而Collection的addAll( )方法只能将其追加到末尾。
- ListIterator是一个更加强大的Iterator的子类型,它只能用于List类的访问。相比于Iterator只能向前移动,它可以双向移动。
可以向前遍历:
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>(5);
List<Integer> list2 = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
list1.add(i);
}
System.out.println("list1:" + list1); // list1:[0, 1, 2, 3, 4]
ListIterator<Integer> it = list1.listIterator(5); // 将初始指针置于末尾
while (it.hasPrevious()) {
list2.add(it.previous());
it.set(0); // 调用set()方法替换当前指针访问的值
}
System.out.println("list2:" + list2); // list2:[4, 3, 2, 1, 0]
System.out.println("list1:" + list1); // list1:[0, 0, 0, 0, 0]
}
}
- 使用字符串调用charAt(index)方法可返回索引为index处的字符。
- Map使用get(key)方法返回key所对应的value,若容器中不存在该key,则返回null。
- LinkedList提供了方法支持队列的行为,并且实现了Queue接口,因此它可以作为Queue使用。
Queue<Integer> queue = new LinkedList<>();
LinkedList提供的Queue相关的方法有:
queue.offer(1); // 将一个元素插入队尾,或者返回false
queue.peek(); // 在不移除的情况下返回队头,队列为空返回null
queue.element(); // 在不移除的情况下返回队头,队列为空抛出NoSuchElementException异常
queue.poll(); // 移除并返回队头,队列为空返回null
queue.remove(); // 移除并返回队头,队列为空抛出NoSuchElementException异常
- 到目前为止,foreach语法主要用于数组,但也可以用于任何Collection对象。事实上,只要是实现了Iterable接口的类,都可以使用foreach进行遍历。
class IterableClass implements Iterable<String> {
private String[] words = "I am a Chinese I love China !".split(" ");
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < words.length;
}
@Override
public String next() {
return words[index++];
}
};
}
public static void main(String[] args) {
IterableClass iterableClass = new IterableClass();
for (String s : iterableClass) {
System.out.print(s + " "); // I am a Chinese I love China !
}
}
}
上面为IterableClass类实现了Iterable接口,并重写Iterator方法返回了一个前向迭代器用于foreach,下面为其添加一个反向迭代器,使得foreach可以反向输出。
class IterableClass implements Iterable<String> {
private String[] words = "I am a Chinese I love China !".split(" ");
// 默认迭代器
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < words.length;
}
@Override
public String next() {
return words[index++];
}
};
}
// 反向迭代器,使用Iterable包装。
public Iterable<String> reverse() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
int index = words.length - 1;
@Override
public boolean hasNext() {
return index > -1;
}
@Override
public String next() {
return words[index--];
}
};
}
};
}
public static void main(String[] args) {
IterableClass iterableClass = new IterableClass();
for (String s : iterableClass) {
System.out.print(s + " "); // I am a Chinese I love China !
}
System.out.println();
for (String s : iterableClass.reverse()) {
System.out.print(s + " "); // ! China love I Chinese a am I
}
}
}
我们看到Iterator其实已经有很多处理集合元素相关的方法了,为什么还需要抽象一层呢?很多集合不直接实现Iterator接口,而是实现Iterable?
因为Iterator接口的核心方法next()或者hasNext() 是依赖于迭代器的当前迭代位置的。 如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。
但即使这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。多个迭代器是互不干扰的。
另外,这种选择也是解耦合的一种,我们可以将集合实例对象与具体的遍历方法解耦,实例对象只需调用Iterator方法返回一个Iterator对象,其他的事交给这个对象即可。