我们看下边这张集合的关系图,橙色的是接口,蓝色的是实现类。下边我们将对这些接口和实现类进行一一介绍:
集合和数组一样,都是java提供的一种容器。集合不能存储基本数据类型数据,只能用来存储对象,但是它可以用来存储多个不同类型的数据,下面我们看下集合和数组的区别。
集合和数组的区别:
数组:长度固定不可变,同一数组只能存储同一类型的元素。集合:长度可变,同一集合可以存储不同类型的元素。在实际开发中,数组一般存储基本数据类型,集合一般存储对象,因为集合不能存储基本数据类型的元素,集合存储基本数据类型的数据需要使用基本数据类型的包装类。
集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection和双列集合java.util.Map,
Collection 是单列集合的根接口,用于存储一系列符合某种规则的元素,他有三个重要的子接口,分别是 List接口、Set接口、Queue接口(不常见)。其中 List集合的特点是 元素有序、可重复,Set集合的特点是 元素无序、不可重复。
Map 是双列集合的根接口,用于存储一系列的键值对(key-value),一组键值对是 Map集合的一个元素,通过键可以找到对应的值。其中值得注意的是:Map集合中的 键不可重复,值可重复,一个键只对应一个值。
Collection是单列集合的根接口,所以它定义了一些Set接口和List接口的常用方法,Collection根接口的方法可以操作所有的单列集合。
public boolean add(E e): 将指定的对象添加到集合中。
public boolean remove(E e): 从集合中删除指定的元素。
public boolean contains(E e): 判断当前集合中是否包含指定的对象元素。
public boolean isEmpty(): 判断当前集合是否为空,为空返回true。
public int size(): 返回集合中元素的个数。
public Object[] toArray(): 把集合中的元素,存储到数组中。
public void clear() : 清空集合中所有的元素。
Iterator<E> iterator(): 返回 Collection集合的迭代器
Iterator接口被Collection接口所实现,迭代器的作用就是用来遍历集合中的元素的!
public E next():返回迭代的下一个元素。
public boolean hasNext():如果仍有元素可以迭代,则返回 true。
下面我们通过一段代码,来演示以下Collection集合的常用方法及Iterator迭代器的使用:
public static void main(String[] args) { // 创建一个ArrayList集合对象,这里使用了多态,父类的引用指向子类的实现。忘记的同学可以去复习下面向对象三大特性 Collection collection = new ArrayList(); collection.add("小明"); // 添加 String类型元素 collection.add(11); // 添加 int类型元素,这里会被转换成Integer包装类型,因为集合中不能存储基本数据类型 collection.add(12.2); // 添加 Double包装类型元素 collection.add(true); // 添加 Boolean包装类型元素 System.out.println("判断集合中元素是否为空,为空返回true:" + collection.isEmpty()); // false System.out.println("判断集合中是否包含指定元素:" + collection.contains(11)); // true System.out.println("返回集合中元素的个数:" + collection.size()); // 4 collection.remove(11); // 删除集合中指定元素 System.out.println("删除元素11后,集合中元素个数:" + collection.size()); // 3 // 以下进行迭代器是使用 // 获取 collection集合对象的迭代器 Iterator iterator = collection.iterator(); // 使用while循环遍历集合中元素 while (iterator.hasNext()) { // hashNext() 判断是否存在下一个元素,存在返回true,不存在返回false // next() 获取下一个元素,并将指针指向下一个元素 System.out.println(iterator.next()); // 打印结果:小明 12.2 true } // 将collection集合转换成数组,这里的toArray()方法,在内部使用类copy方法,所以它操作的是collection集合的副本 Object[] objects = collection.toArray(); // 遍历数组中元素 for (Object obj : objects) { System.out.println(obj); // 打印结果:小明 12.2 true } // 清空集合中所有元素 collection.clear(); System.out.println("清空集合中元素后,查看集合是否为空:" + collection.isEmpty()); // true }
List接口继承Collection接口,它是单列集合的一个重要分支,我们习惯性的将实现类List接口的对象叫做 List集合。
List集合的特点是:元素可重复、元素有序;这和数组的特点是很像的,而它的实现类ArrayList,底层正是维护了一个数组。下面 ArrayList 源码解读会给大家详细分析。
List有以上特点,是因为集合中的所有元素都是以一种线性方式存储的,在程序中可以通过索引来访问集合中的指定元素;并且集合中元素的存储顺序与取出顺序相同,这些都是数组的特点,再次强调,因为 ArrayList的底层正是维护了一个 final Object类型的数组。
List接口特点总结:
1、List集合中的元素时存取有序的。比如存入顺序是1、2、3、4,那么存储元素的顺序也是按照1、2、3、4来完成存储的。
2、List集合中的元素可重复。可以通过 equals方法来比较是否是重复元素。
3、List集合带索引,通过索引就可以精确的操作List集合中的元素了,它的索引与数组一个道理。
List接口作为 Collection接口的子接口,他不仅继承了Collection接口中的所有方法,还有自己的一套方法,主要是一些根据索引来操作集合中元素的方法:
public void add(int index, E element): (添加元素)将指定的元素,添加到该集合中的指定位置上。
public E remove(int index): (删除元素)移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element): (更新元素)用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
public E get(int index): (查找元素)返回集合中指定位置的元素。
java.util.ArrayList集合的数据存储结构是数组结构。元素增删慢,查找快。由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
java.util.ArrayList 是大小可变的数组的实现,它是List集合的一个实现类, ArrayList 中可以不断添加元素,其大小也自动增长。
public boolean add(E element) : 将元素添加到集合的尾部
public boolean add(int index,E element) : 将元素添加到集合的指定位置
public E remove(int index) : 删除集合中指定位置的元素
public boolean remove(Object o) : 删除集合中首次出现的指定的元素
public void clear() : 清空集合中的所有元素
public boolean contains(Object o) : 判断集合中是否包含指定的元素,存在返回true
public E get(int index) : 返回集合中指定位置的元素
public boolean isEmpty() : 判断集合中是否存在元素,如果没有元素返回true
public int indexOf(Object o) :返回集合中首次出现指定元素的索引位置,如果不存在返回-1
public int lastIndexOf(Object o) : 返回集合中最后一次出现指定元素的索引位置,不存在返回-1
public E set(int index,E element) : 用指定元素替换指定位置上的元素,返回被替换的元素值
public Object[] toArray() : 将集合转换成数组,操作的是集合的副本,不影响集合本身。
public void trimToSize() : 将此 ArrayList 实例的容量调整为列表的当前大小。应用程序可以使用此操作来最小化 ArrayList 实例的存储量。
public int size(): 返回集合中元素的数量
下面我们来阅读一段 ArrayList的源码,来看看 ArrayList集合是怎么实现数组长度大小可变的。
// 默认初始容量为10 private static final int DEFAULT_CAPACITY = 10; // ArrayList底层维护了一个 final Object类型的空数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 存储ArrayList元素的数组缓冲区,ArrayList的容量是这个数组缓冲区的长度。 // 任何空的ArrayList集合,也就是使用无参构造方法创建的ArrayList集合,它的初始容量都是 DEFAULT_CAPACITY,也就是10. transient Object[] elementData; // 通过构造方法,构建一个初始容量为10的ArrayList空集合。 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 添加元素,我们可以通过源码来了解ArrayList是怎么实现数组长度大小可变的。 * ensureCapacityInternal:用来保证集合的容量,防止数组越界的发生 * elementData 数组缓冲区,它的长度是ArrayList集合中元素的个数 */ public boolean add(E e) { ensureCapacityInternal(size + 1); // size是当前ArrayList集合中元素个数,将元素个数+1,保证集合的容量 elementData[size++] = e; // 将元素插入到集合的尾部, return true; } // minCapacity是集合中存储元素的最小容量,所以每次像集合中插入元素时,都要+1 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 比较 初始容量10 与 集合中实际元素个数,返回他们之中的最大值。以保证集合可以成功扩容并添加元素 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } 大家要养成阅读源码的习惯,这在企业开发中是很重要的,可能开始阅读起来比较困难,只要坚持,你会发现源码其实也没那么神秘。
java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。
在学习LinkedList集合之前,我们先看一下什么是双向链表?
链表结构是数据结构的一种,和数组一样,它是一种物理结构的数据结构,数据结构分为两大类:物理结构、逻辑结构。
物理结构:就是按照这种结构真实的在内存中存储数据的结构,物理结构的数据结构只有 链表和数组两种,这是最基本的数据结构。
逻辑结构: 是一种假想的不是真实存在的,但是能够更好的帮助我们完成对数据的操作的结构,就像我们的业务逻辑一样。
关于数据结构,包子会单独写一篇文章来说明,这里就简单一提,我们还是先来说说 LinkedList集合。
上图中已经很详细的描述类双向链表的结构以及增删查操作,LinkedList集合就是使用双向链表实现的。
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,LinkedList集合除了继承了List集合的一些对元素的操作方法外,LinkedList提供了大量首尾操作的方法。还这些方法我们作为了解即可:
public boolean add(E e): 向集合的末尾添加元素
public boolean offer(E e): 将指定元素添加到集合的末尾
public boolean add(int index,E e):向集合中指定位置插入指定元素
public void addFirst(E e): 将指定元素插入集合的开头
public void addLast(E e):将指定元素添加到集合的末尾。
public void push(E e): 将元素压入栈中,也就是添加到集合的第一个位置
public E pop(): 将元素从栈中弹出,也就是移除并返回集合中第一个元素。
public boolean offerFirst(E e):向集合开头插入指定元素
public boolean offerLast(E e): 向集合末尾插入指定元素
public E poll(): 获取并移除集合中第一个元素
public E pollFirst(): 获取并移除集合中第一个元素,如果集合为空返回null
public E pollLast(): 获取并移除集合中最后一个元素,如果集合为空返回null
public E remove(): 移除并返回集合的第一个元素
public E remove(int index): 移除并返回列表中指定索引位置的元素
public boolean remove(Object o): 移除集合中首次出现的指定元素,如果存在返回true
public E removeFirst(): 移除并返回集合中第一个元素
public E removeLast(): 移除并返回集合中最后一个元素
public E set(int index,E element): 替换集合中指定位置的元素
public ListIterator<E> listIterator(int index): 返回集合的迭代器(顺序迭代)
public Iterator<E> descendingIterator(): 返回集合的逆序迭代器(逆序迭代)
public E element(): 获取但不移除集合的第一个元素,列表为空抛出异常
public E peek(): 获取但不移除集合的第一个元素,如果列表为空,则返回null
public E peekFirst(): 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
public E peekLast(): 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null
public E get(int index): 返回集合中指定索引位置的元素,超出索引范围抛出异常
public E getFirst(): 返回集合中第一个元素,集合为空抛出异常
public E getLast(): 返回集合中最后一个元素,集合为空抛出异常
public int indexOf(Object o): 返回首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1
public int lastIndexOf(Object o): 返回最后一次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1
public boolean contains(Object o): 如果集合中存在指定元素,则返回true
public int size(): 返回集合中元素的总个数
public boolean isEmpty():如果列表不包含元素,则返回true。
LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用(了解即可)。代码的实现同学们自己去实现吧,还是很简单的。