集合Collection
我们看下边这张集合的关系图,橙色的是接口,蓝色的是实现类。下边我们将对这些接口和实现类进行一一介绍:
集合的概述
集合和数组一样,都是java提供的一种容器。集合不能存储基本数据类型数据,只能用来存储对象,但是它可以用来存储多个不同类型的数据,下面我们看下集合和数组的区别。
集合和数组的区别:
- 数组:长度固定不可变,同一数组只能存储同一类型的元素。
- 集合:长度可变,同一集合可以存储不同类型的元素。
在实际开发中,数组一般存储基本数据类型,集合一般存储对象,因为集合不能存储基本数据类型的元素,集合存储基本数据类型的数据需要使用基本数据类型的包装类。
集合框架
集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection
和双列集合java.util.Map
,
Collection 是单列集合的根接口,用于存储一系列符合某种规则的元素,他有三个重要的子接口,分别是 List接口、Set接口、Queue接口(不常见)。其中 List集合的特点是 元素有序、可重复,Set集合的特点是 元素无序、不可重复。
Map 是双列集合的根接口,用于存储一系列的键值对(key-value),一组键值对是 Map集合的一个元素,通过键可以找到对应的值。其中值得注意的是:Map集合中的 键不可重复,值可重复,一个键只对应一个值。
Collection常用方法
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迭代器
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集合
List介绍
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接口的常用方法
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)
: (查找元素)返回集合中指定位置的元素。
ArrayList 集合
ArrayList介绍
java.util.ArrayList
集合的数据存储结构是数组结构。元素增删慢,查找快。由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList
是最常用的集合。
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
java.util.ArrayList 是大小可变的数组的实现,它是List集合的一个实现类, ArrayList 中可以不断添加元素,其大小也自动增长。
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()
: 返回集合中元素的数量
ArraList源码解读
下面我们来阅读一段 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);
}
大家要养成阅读源码的习惯,这在企业开发中是很重要的,可能开始阅读起来比较困难,只要坚持,你会发现源码其实也没那么神秘。
ArrayList 集合代码演示
public static void testMyArrayList() {
// 构建一个初始值为10的空ArrayList集合(去看源码)
List list = new ArrayList();
System.out.println("查看集合中是否存在元素:" + list.isEmpty());
System.out.println("查看集合中元素个数:" + list.size());
// 向集合中添加元素
list.add("aa"); // 添加String类型的元素
list.add(22); // 添加Integer类型的元素
list.add('a'); // 添加Char类型的元素
list.add(12.22); // 添加Double类型的元素
/*
* 根据索引获取集合中的元素
* 集合中元素的存储顺序与添加顺序是相同的,可以通过索引访问指定位置的元素,一旦索引越界就
*会报异常。
*/
System.out.println("索引值为0的元素" + list.get(0)); // aa
System.out.println("索引值为1的元素" + list.get(1)); // 22
System.out.println("索引值为2的元素" + list.get(2)); // a
System.out.println("索引值为3的元素" + list.get(3)); // 12.22
// java.lang.IndexOutOfBoundsException 索引越界异常
// System.out.println("索引值为4的元素" + list.get(4));
// 向集合中指定位置添加元素
list.add(2, "aa");
list.add(0, 22);
list.add(3, 2);
/*
* 向集合最后一个位置添加元素,之前最后一个位置的元素应该是12.22,但是我们向最后一个位置
*添加元素后,这个位置的值被'a'取代了,12.22会向后移动一位
* 所以集合中的元素排序应该是 最后一位是12.22,倒数第二位是‘a’,我们来查看一下对不对
*/
list.add(list.size() - 1, 'a'); // 向当前集合最后一个位置添加元素a
// 22,因为我们向索引0位置添加了元素22,所以返回值是22
System.out.println("索引值为0的元素" + list.get(0));
System.out.println("当前集合中元素个数:" + list.size()); // 8
System.out.println("索引值为6的元素(最后一位元素)" + list.get(7)); // 12.22
// 返回值是 a 结果证明我们的猜想是对的
System.out.println("索引值为5的元素(倒数第二位元素)" + list.get(6)); // a
// 遍历list集合中的元素
for (Object obj : list) {
System.out.println(obj); // 当前集合中元素的顺序是 :22 aa 22 2 aa a a 12.22
}
/*
* remove(int index)、remove(Object o)
* 当前集合中元素的顺序是 :22 aa 22 2 aa a a 12.22
* 值得注意的是,remove方法有两个重载方法,一个是移除指定索引位置的元素,一个是移除第一
*次出现在集合中的指定元素.
* 当要被移除的元素是整数时,list集合会调用按索引删除的remove方法。
* 当我们要删除集合中的整数时,需要先使用找到要删除元素的索引,然后按索引删除可。
* 以上是包子自己发现的一个有趣的结论,可能不正确,知道内部原理的大佬欢迎留言指正,包子
*会及时进行修改
*
* */
// 删除元素中第一个出现的元素22
int i = list.indexOf(22);// 元素22第一次出现的索引位置
// 返回值是被删除的元素值,22
System.out.println("根据索引删除第一个出现的元素2:" + list.remove(i));
// 删除元素中最后一个出现的元素22
int j = list.lastIndexOf(22);// 元素22最后一次出现的索引位置
// 返回值是被删除的元素值,22
System.out.println("根据索引删除第一个出现的元素2:" + list.remove(j));
// 替换指定位置的元素值
System.out.println("集合中索引为3的位置的元素是:" + list.get(3));
System.out.println("将集合中索引为3的位置的元素替换成‘哈哈’,返回值是被替换的元素值:" + list.set(3, "哈哈"));
System.out.println("替换后集合中索引值为3的元素是:" + list.get(3));
// 查看集合中是否包含元素 "哈哈",存在返回true,否则返回false
System.out.println("集合中是否存在元素‘哈哈’?" + list.contains("哈哈")); //true
// 清空集合中所有元素
list.clear();
System.out.println("被清空后集合中元素数量:" + list.size()); // 0
}
LinkedList集合
java.util.LinkedList
集合数据存储的结构是链表结构。方便元素添加、删除的集合。
在学习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集合也可以作为堆栈,队列的结构使用(了解即可)。代码的实现同学们自己去实现吧,还是很简单的。