前言
此篇为记录Java基础中集合章节的有关知识点,主要包括Collection接口、Map接口下的实现类,以及他们之间的联系和区别
一、集合概要及框架体系
1、集合的理解
- 集合跟数组一样,它也是用来保存数据的。那么为什么还会出现集合呢?那是因为数组有许多不足的地方。集合主要是两组(单列集合、双列集合)
2、数组的缺点
- 长度开始时必须指定,而且一旦指定,不能更改。
- 保存的数据类型必须为同一类型的元素。
- 使用数组进行增加/删除元素时比较麻烦(每次都要进行扩容或者缩减)
3、集合的好处
- 可以动态保存任意类型的对象或元素,使用方便
- 提供一系列的操作对象的方法,不用自己实现它,可以直接使用,进行添加删除新元素方便。
4、集合的框架体系
1、Java的集合类很多,主要有Collection 接口和 Map 接口两大类,它们都继承了 Iterable 接口。
2、Collection接口 最常见的是Set 和List 接口;
- Set接口中的常用实现类有:HashSet、TreeSet
- List接口中的常用实现类有:ArrayList 、LinkedList、Vector,其中Vector的子类有常见的Stack(栈)类
3、Map接口中常见的实现类有HashMap、TreeMap、HashTable
- HashMap常用的继承子类有LinkedHashMap
- HashTable常用的继承子类有Properties
二、Collection集合(单列集合)
Collection集合特点
- Collection 实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类可以存放重复的元素,有些不可以
- 有些Collection 的实现类是有序的(List),有些不是有序(Set)
- Collection接口没有直接实现的子类,是通过它的子接口List和Set来实现的
Collection集合遍历元素方式
1、迭代器的遍历方式
//以hashSet为例:
HashSet hashSet = new HashSet();
Iterator iterator = hashSet.iterator();//1. 先得到 col 对应的 迭代器
//使用 while 循环遍历,此循环可以使用 itit 快捷键
//显示所有的快捷键的的快捷键 ctrl + j
while (iterator.hasNext()) {//判断是否还有数据
Object obj = iterator.next();//返回下一个元素,类型是 Object
System.out.println(obj);
}
//当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素 iterator.next();
//NoSuchElementException
//如果希望再次遍历,需要重置我们的迭代器
iterator = hashSet.iterator();
注意:
- 多次使用此迭代器时需要重置一下,因为每次循环完之后迭代器的
- 在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。
2、增强for循环的遍历方式
//以HashSet为例
HashSet hashSet = new HashSet();
for (Object obj : hashSet) {
//此时可用快捷键 hashSet.I
System.out.println(obj);
}
1、List
List 接口是Collection接口的子接口。
- List集合内的元素是有序的(添加顺序和取出顺序一致)、且可以重复。
- List集合的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素是都对应一个整数型的序号记载其在容器的位置
Ⅰ. ArrayList底层结构和源码分析
一、注意事项
- ArrayList底层是由数组实现数据存储的。
- ArrayList基本等同于Vector,除了ArrayList是线程不安全的(执行效率高),而Vertor是线程安全的。不建议使用ArrayList.
二、底层操作机制源码分析
扩容机制结论:
-
ArrayList 底层维护了一个Object类型的数组elementData。
-
当创建ArrayList对象时它有两种构造器,一种是无参构造,另一种是有参构造。当它创建时的长度为0时使用无参构造,则扩容elementData为10,如需要再次扩容,则是elementData的1.5倍。
-
当创建elementData给定一个长度时用有参构造,如需要扩容,则直接扩容为elementData的1.5倍。
-
在添加元素的时候,先调用特定的方法来确定是否要扩容(每次都要看是否需要扩容)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
确定要扩容之后把需要扩容的大小设置为10,然后表示扩容的次数的变量自增1,如果大小不够,就调用grow()扩容(扩容到原来的1.5倍(length + length >> 1)),容量够了就不会调用grow()方法了。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow()方法源码:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
Ⅱ Vector 底层结构和源码分析
一、注意事项
- Vector集合跟ArrayList底层大致相同,只不过Vector跟ArrayList相比,它的线程安全(synchronized)
- 它的扩容机制也跟ArrayList类似,在扩容时如果是没有给定大小,那么第一次扩容为10,但是第二次开始是按2倍扩容。
- 与ArrayList相比,它的线程安全,但执行效率稍低。
二、debug源码
//底层同样跟ArrayList是elementData数组
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
/**二倍扩容机制
*/
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
Vector子类——Stack(栈)
Ⅲ LinkedList底层结构和源码分析
一、注意事项
- LinkedList中维护了两个属性,first和last ,first指向第一个节点Node,Last指向最后一个节点Node。
- 每个Node节点中有prev(指向前一个节点)、next(指向下一个节点)、item(内容)这三个属性,最终实现双向链表
- LinkedList底层维护了一个双向链表,而不是数组,所以添加和删除效率比ArrayList和Vector都高。
二、DebugLinkedList的添加元素 源代码
//DebugLinkedList的添加元素 源代码
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l