集合
第一章Collection
前言
Java封装了大量的工具类例如本章的集合集合就是其一。集合是在面试中和日常学习中的出现频率都是很高的,在此简单描述其使用方法是实现原理及流程,为减少篇幅和便于理解,均只做流程演示,底层hash算法等复杂部分亦或者更深入的带native方法后期会出专题介绍。希望能给各位带来帮助,原创不易望支持!
一、Collection
1.简介
- Collection继承自Iterable
- 包含两个子接口分别是List和Set
- List 的常用实现类有ArrayList,LinkedList和Vector
- Set的常用实现类有HashSet和TreeSet
- 可以使用索引
2.遍历
1.实现Collection的类都可以使用迭代器进行遍历
注意在使用迭代器遍历一遍后应重置迭代器然后再次去获取否则会抛出NoSuchelementExcepiton异常
Iterator<T> iterator();//迭代器
2.使用增强for循环遍历
增强for我们也可以用来遍历数组元素,底层我们可以看出依旧是调用的迭代器
调用步骤依依次是下图
for (Object objet : list) {}//底层实现依然是迭代器
new iterator
HashNext();
next();
3.方法
Collection的实现类方法有很多例如 add() 自动装箱 等都是Collection通用的的方法
3.List
List 添加顺序与取出顺序一致,且可以重复,有对应索引,支持使用索引取出元素。且可以存储空值null
3.1.ArrayList
基本等同于vector,ArrayList执行效率高,但是线程不安全。多线程场景不建议使用ArrayList,建议使用Vector,因为他有synchronized关键字修饰。ArrayList继承自 AbstractList 实现了 List, RandomAccess, Cloneable, Serializable
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
3.1.2存储
ArrayList中维护的是一个叫elementData的数组,其默认大小为0
transient Object[] elementData; // non-private to simplify nested class access
//transient关键字表示该成员不会被序列化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
this.elementData = null
3.1.3扩容机制
elementData默认容量为0,添加元素时,先调用 checkPackageAccess ,通过哈希得到哈希值作为指向进行存储。第一次增加数据默认扩容为10,再次扩容为1.5倍
public void add(int index, E element) {
this.rangeCheckForAdd(index);
++this.modCount;
int s;
Object[] elementData;
if ((s = this.size) == (elementData = this.elementData).length) {
elementData = this.grow();
}
跟着调用rangeCheckForAdd方法确定是否扩容,
private void rangeCheckForAdd(int index) {
if (index > this.size || index < 0) {
throw new IndexOutOfBoundsException(this.outOfBoundsMsg(index));
}
}
确认调用后然后调用add()向下调newCapacity将数组扩容为10
private int newCapacity(int minCapacity) {
int oldCapacity = this.elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity);
} else if (minCapacity < 0) {
throw new OutOfMemoryError();
} else {
return minCapacity;
}
} else {
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
}
}
当达到10个元素时再次扩容调用流程add>rangeCheckForAdd>...newCapacity扩容后为15
//扩容index
private int newCapacity(int minCapacity) { //minCapacity:11
int oldCapacity = this.elementData.length; //oldCapacity:10
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity);
} else if (minCapacity < 0) {
throw new OutOfMemoryError();
} else {
return minCapacity;
}
} else {
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
}
}
扩容数组,通过数组的工具类arraycopy等实现复制迁移原数组数据
System.arraycopy(elementData, index, elementData, index + 1, s - index);
elementData[index] = element; //elementData: object[15]
this.size = s + 1;
3.2LinkedList
LinkedList底层维护了一个双向链表和双端队列,同样他也是线程不安全的
3.2.1实现
在LinkedList中维护了有三个比较重要的属性 prev,next,item 下文以添加数据为例
public boolean add(E e) {
this.linkLast(e);
return true;
}
add调用linkLast,空则新建链表
void linkLast(E e) {
LinkedList.Node<E> l = this.last;
LinkedList.Node<E> newNode = new LinkedList.Node(l, e, (LinkedList.Node)null);
this.last = newNode;
if (l == null) {
this.first = newNode;
} else {
l.next = newNode;
}
可以看出Node是维护了一个数据节点,里面包括了头节点,尾节点,和数据本身 ,在LnkedList中均为Node组成的链表,头尾节点地址采用hashCode得出哈希值。在第二次添加时新节点的头节指向旧的尾节点。
private static class Node<E> {
E item;
LinkedList.Node<E> next;
LinkedList.Node<E> prev;
Node(LinkedList.Node<E> prev, E element, LinkedList.Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
3.3Vector
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
Vector继承AbstractList实现了List, RandomAccess, Cloneable, Serializable
vector基本同ArrayList,底层维护一个数组 protected Object[] elementData;
3.3.1线程安全的
在vector中的大部分成员均采用protected修饰,且大部分操作方法均有synchronized锁,所有他是线程安全的
3.3.2扩容
首先使用默认无参构造器进行扩容,默认容量为10,容量上限达到后呈两倍增长
public Vector(int initialCapacity, int capacityIncrement) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
} else {
this.elementData = new Object[initialCapacity]; //initialCapcity:10
this.capacityIncrement = capacityIncrement;
}
}
4. Set接口
- set接口有两个实现类分别是HashSet和LinkdHashSet
- 因为都是Collection的实现,所有也可以使用迭代器进行遍历
- Set是无序的,添加或者取出顺序不一致,不存在索引
- 不允许重复元素,至多一个空值null
4.1HashSet
实现了Set接口,底层仍然是套用的HashMap,只能存放一个null,不保证元表有序,取决于hash后的索引,不能有重复元素
4.1.1实现
hashSet底层维护了一个单向链表当链表元素到达8,table>=64时将进行树化(1.8新特性),树为红黑树
![红黑树](https://i-blog.csdnimg.cn/blog_migrate/0ff00b9d0830b8feb973b11ef5f43b9b.png)
4.1.2添加元素
- add()添加元素
- 调用map.put()
- 在添加前先hash()得到hash值转为索引值
- 判断是否冲突,若有相同就使用equals进行比较
- 没有直接放入数组,添加到列表最后
4.2LinkedHashSet
inkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, Serializable
inkedHashSet继承HashSet实现Set<E>, Cloneable, Serializable
大量调用LinkedHashMap,底层维护了一个数组+双向链表,在节点中维护before和after属性。因为是双向链表想达到O(1)的时间复杂度,插入取出依旧是有序的,且允许null值
4.2.1插入实现
先使用integer自动装箱,求hash值,再求索引,确定在HashTabe中的位置,如果已经存在该索引则无法添加
4.3TreeSet
可以对值进行比较,构造器重写了compare
底层实现同TreeMap
5.工作中如何选择Collection的实现类
线程安全 | vector |
线程不安全 | Linked | ArrayList |
改查占比多 | ArrayList |
增删占比多 | LinkedList |
二、Map
用于保存具有映射关系的数据,以K,V对的形式存储(常使用string作K)
Map中的KV可以为任意类型,KV对会被封装到HashMap$Node中
K不可以重复,而V可以重复
KV都可以为null,但是K只可以存在一个null
KV为单向一对一关系
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。