集合框架介绍
说明:对于以上的框架图有如下几点说明
- 所有集合类都位于
java.util
包下。Java
的集合类主要由两个接口派生而出:Collection
和Map
,Collection
和Map
是Java
集合框架的根接口,这两个接口又包含了一些子接口或实现类。 - 集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。
- 抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。
- 实现类:8个实现类(实线表示),对接口的具体实现。
Collection
接口是一组允许重复的对象。Set
接口继承Collection
,集合元素不重复。List
接口继承Collection
,允许重复,维护元素插入顺序。Map
接口是键-值对象,与Collection
接口没有什么关系。Set
、List
和Map
可以看做集合的三大类:
List
集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
Set
集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是集合里元素不允许重复的原因)。
Map
集合中保存Key-value
对形式的元素,访问时只能根据每项元素的key
来访问其value
。
0 准备知识
- 集合底层采用数组方式
- 怎么保证集合存放无限大小 -
数组扩容技术
数组扩容方式一:
Arrays.copyOf(objects, 10);
- 参数一:需要扩容的数组对象
- 参数二:设置新数组的长度
package com.snow.list;
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Object[] objects = { 1, 2 };
System.out.println("扩容前数组长度 --- " + objects.length);
for (Object o : objects) {
System.out.println("扩容前数组元素 --- " + o);
}
System.out.println("################ 扩容后 #################");
// 返回新的数组,将原来的数组的长度为2,现在扩容为10,原来本身的数据不变.
Object[] copyNewObjects = Arrays.copyOf(objects, 10);
System.out.println("扩容后数组长度 --- " + copyNewObjects.length);
for (Object n : copyNewObjects) {
System.out.println("扩容后数组元素 --- " + n);
}
}
}
控制台打印:
扩容前数组长度 --- 2
扩容前数组元素 --- 1
扩容前数组元素 --- 2
################ 扩容后 #################
扩容后数组长度 --- 10
扩容后数组元素 --- 1
扩容后数组元素 --- 2
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
扩容后数组元素 --- null
数组扩容方式二:
System.arraycopy(src, srcPos, dest, destPos, length);
- 参数一:src 原数组
- 参数二:srcPos 起始位置
- 参数三:dest 目标数组
- 参数四:destPos 目标起始位置
- 参数五:length 复制长度
package com.snow.list;
public class Test {
public static void main(String[] args) {
int[] fun = { 0, 1, 2, 3, 4, 5, 6 };
System.arraycopy(fun, 3, fun, 0, 2);
// 3423456
for (int i : fun) {
System.out.print(i);
}
}
}
控制台打印:
3423456
说明:目标数组还是原来的数组,只是将原数组从索引为 3 的位置开始赋值到新数组中,没有赋值的还是原数组的值。
位算法:
package com.snow.list;
public class Test {
public static void main(String[] args) {
// 原来本身elementData容量大小 2
int oldCapacity = 2;
// 新数据容量大小 (oldCapacity >> 1)=oldCapacity/
int newCapacity = oldCapacity + (oldCapacity >> 1);// (2+2/2)=3
System.out.println(newCapacity);
}
}
控制台打印:
3
1 JDK
数组源码说明
-
JDK 1.7
之后 数组默认数据大小代码存放在add
方法 (JDK 1.6
的时候 默认构造函数初始化elementData
大小) -
Arraylist
底层采用数组实现 数组名称elementData
-
Arraylist
底层数组默认初始化为10
2 List
框架
List
集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List
集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。
List
接口继承于 Collection
接口,它可以定义一个允许重复的有序集合。因为 List
中的元素是有序的,所以我们可以通过使用索引(元素在 List
中的位置,类似于数组下标)来访问 List
中的元素,这类似于 Java
的数组。
List
接口为 Collection
直接接口。List
所代表的是有序的 Collection
,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现 List
接口的集合主要有:
ArrayList
LinkedList
Vector
Stack
3 ArrayList
底层实现原理
Arraylist
底层基于数组实现
private Object[] elementData;
Arraylist
底层默认数组初始化大小为 10
个 object
数组
public ExtArraylist() throws Exception {
this(10);
}
public ExtArraylist(int initialCapacity) throws Exception {
if (initialCapacity < 0) {
throw new IllegalArgumentException("初始容量不能小于0 " + initialCapacity);
}
elementData = new Object[initialCapacity];
}
添加元素后大于当前数组的长度,则进行扩容,将数组的长度增加原来数组的一半。
// 增大数组空间
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 在原来容量的基础上加上
// oldCapacity/2
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // 最少保证容量和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);
}
4 手写 ArrayList
4.1 接口
package com.snow.list;
/**
* 自定List接口<br>
*
* @param <E>
*/
public interface ExtList<E> {
void add(E object);
void add(int index, E object);
Object remove(int index);
boolean remove(E object);
int getSize();
Object get(int index);
}
4.2 实现类
package com.snow.list;
import java.util.Arrays;
/**
* 纯手写ArrayList<br>
*
*/
public class ExtArrayList<E> implements ExtList<E> {
// 保存ArrayList中数据的数组
private transient Object[] elementData;
// ArrayList实际数量
private int size;
public ExtArrayList() {
// 默认初始容量为10
this(10);
}
public ExtArrayList(int initialCapacity) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
// 初始化数组容量
elementData = new Object[initialCapacity];
}
/**
* 添加方法实现
*
* @param object
*/
public void add(Object object) {
ensureExplicitCapacity(size + 1);
elementData[size++] = object;
}
/**
* 添加方法实现
*
* @param object
*/
public void add(int index, Object object) {
rangeCheck(index);
ensureExplicitCapacity(size + 1);
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = object;
size++;
}
// 扩容
private void ensureExplicitCapacity(int minCapacity) {
// 如果存入的数据,超出了默认数组初始容量 就开始实现扩容
if (size == elementData.length) {
// 获取原来数组的长度 2
int oldCapacity = elementData.length;
// oldCapacity >> 1 理解成 oldCapacity/2 新数组的长度是原来长度1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1); // 3
if (newCapacity < minCapacity) {
// 最小容量比新容量要小的,则采用初始容量minCapacity
newCapacity = minCapacity;
}
// System.out.println("oldCapacity:" + oldCapacity + ",newCapacity:"
// + newCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
/**
* 获取数据
*
* @param index
* @return
*/
public Object get(int index) {
rangeCheck(index);
return elementData[index];
}
/**
* 删除数据
*
* @param index
* @return
*/
public Object remove(int index) {
Object object = get(index);
int numMoved = elementData.length - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
elementData[--size] = null;
return object;
}
public boolean remove(E object) {
for (int i = 0; i < elementData.length; i++) {
Object element = elementData[i];
if (element.equals(object)) {
remove(i);
return true;
}
}
return false;
}
private void rangeCheck(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException("数组越界啦!");
}
}
public int getSize() {
return size;
}
}
4.3 测试
package com.snow.list;
public class Test {
public static void main(String[] args) {
ExtArrayList extArrayList = new ExtArrayList(2);
extArrayList.add("张三");
extArrayList.add("李四");
extArrayList.add("王武");
for (int i=0; i<extArrayList.getSize(); i++) {
System.out.println(extArrayList.get(i));
}
}
}
控制台打印:
张三
李四
王武
5 Vector
底层实现原理
Vector
是线程安全的,但是性能比 ArrayList
要低。
ArrayList
,Vector
主要区别为以下几点:
Vector
是线程安全的,源码中有很多的synchronized
可以看出,而ArrayList
不是。导致Vector
效率无法和ArrayList
相比;ArrayList
和Vector
都采用线性连续存储空间,当存储空间不足的时候,ArrayList
默认增加为原来的50%,Vector
默认增加为原来的一倍;Vector
可以设置capacityIncrement
,而ArrayList
不可以,从字面理解就是capacity
容量,Increment
增加,容量增长的参数。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //扩充的空间增加原来的50%(即是原来的1.5倍)
if (newCapacity - minCapacity < 0) //如果容器扩容之后还是不够,那么干脆直接将minCapacity设为容器的大小
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //如果扩充的容器太大了的话,那么就执行hugeCapacity
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}