前言
集合跟数组一样,也是容器。集合与数组的区别:
- 数组长度固定,存同一类型元素,可以存基本数据类型。
- 集合长度可变,可存不同类型元素,存储的都是对象, 基本数据类型会自动装箱为对象类型。
jdk1.0版本中提供的可用容器比较少,到了jdk1.2版本,为了满足更多的需求,出现了更多的集合来完成不同的需求。这些容器如何区分?每一种容器的数据结构不同。
学习集合体系原则:看顶层,用底层。
正文
一,集合概述
- 集合体系有一个完整的框架图,如下图。其中该体系最顶层的就是Collection接口,该接口中定义了集合体系中最共性的功能:增删元素等。
- 集合中的元素取出方式也是所有集合类必须的具备的功能,将这些操作抽取出来,形成一个公共的接口Iterator,所有集合类中都有一个内部类来实现这个接口,并在集合类(外部类)中提供一个返回内部类对象的方法,从而调用内部类的迭代器方法。
- 集合中存的是对象的引用(地址)。
-
集合中什么是重复元素?即同一个引用(地址)。
二,Collection接口
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递collection,并在需要最大普遍性的地方操作这些 collection。
所有通用的 Collection 实现类(通常通过它的一个子接口间接实现 Collection)应该提供两个“标准”构造方法:一个是 void(无参数)构造方法,用于创建空 collection;另一个是带有 Collection 类型单参数的构造方法,用于创建一个具有与其参数相同元素新的 collection。实际上,后者允许用户复制任何 collection,以生成所需实现类型的一个等效 collection。尽管无法强制执行此约定(因为接口不能包含构造方法),但是 Java 平台库中所有通用的 Collection 实现都遵从它。
三,迭代器(重点)
上面说到,Collection接口定义了集合这一数据结构该有的最基本的方法,包括下列方法:
- 添加元素的方法:add(),addAll()。
- 删除元素的方法:remove(),removeAll(),retainAll(),clear() 。
- 判断元素是否在集合中的方法:contains(),containsAll()。
- 集合的属性方法:isEmpty(),size(),hashCode()。
- 判断集合对象是否相同的方法:equals()。
- 获取迭代器对象的方法:iterator()。
这里重点介绍一下iterator()这个方法,这个方法的原型:
Iterator<E> iterator()
该方法返回的是Iterator接口类型,该接口定义了3个方法
- hasNext() ,如果仍有元素可以迭代,则返回 true。
- next() ,返回迭代的下一个元素。
- remove(),从迭代器指向的 collection 中移除迭代器返回的最后一个元素。
下面,我们来看看集合的实现类ArrayList是怎么实现集合接口和迭代器接口的,下面是部分源码:
public class ArrayList<E> implements List<E>{
//省略一些成员变量和方法
public Iterator<E> iterator() { // 这是重写的iterator()方法
return new Itr(); // 返回私有内部类对象
}
private class Itr implements Iterator<E> { // 内部类实现Iterator接口
// 省略内部类的成员变量和方法
public boolean hasNext() {...}
public E next() {...}
public void remove() {...}
}
}
所以,我们在对集合进行迭代的时候,会进行类似下面的操作:
List<String> list = new ArrayList<>(); // 创建集合
list.add("jimmy1"); // 添加元素
list.add("jimmy2");
list.add("jimmy3");
list.add("jimmy4");
Iterator<String> itr = list.iterator(); // 获得迭代器对象
while (itr.hasNext()) { // 使用迭代器对象的方法进行迭代
String string = itr.next();
System.out.println(string);
}
当然,如果只是迭代获得元素,还可以用更简单的foreach循环。
我们还说过,有时候我们需要在迭代的时候操作元素,如增删改操作。但是获得迭代器时长度是已知的,迭代中改变集合长度会造成混乱,一些Iterator可能会抛出ConcurrentModificationException异常。针对这种情况,List接口使用了新的迭代器ListIterator,该迭代器继承了Iterator迭代器,来看一下:
public interface ListIterator<E> extends Iterator<E>
同样的,ArrayList等实现类在实现该接口也是使用内部类来实现的,这样,List集合就能实现边迭代边修改元素了。下面是ListIterator的实现类。
public class ArrayList<E> implements List<E>{
//省略一些类成员变量和方法
public Iterator<E> iterator() { // 这是重写的iterator()方法
return new Itr(); // 返回私有内部类对象
}
public ListIterator<E> listIterator() { // 这是重写的listIterator()方法
return new ListItr(0); // 返回私有内部类对象
}
private class Itr implements Iterator<E> { // 内部类实现Iterator接口
// 省略内部类的成员变量和方法
public boolean hasNext() {...}
public E next() {...}
public void remove() {...}
}
// 内部类实现ListIterator接口
private class ListItr extends Itr implements ListIterator<E> {
// 类实现
}
}
总结
集合很常用,本文介绍了最顶层的接口Collection,并且重点介绍了同样是所有集合的共性的迭代器。