集合介绍
龚德浙
java SE阶段:
Java中常见API:集合、IO相关(基础使用、原理、应用场景)
多线程、网络、数据库(MySQL、Redis)、java虚拟机、设计模式、
spring、SPringMVC、mybatis等
项目:基于SSM整合的博客系统
集合
数组、链表、栈、队列等..通过数据结构,用来存储数据
集合在java.util包路径下
在该包路径下提供了很多的接口及实现类
注意:虚线对应的是接口,实现对应的是实现类
基于集合框架图可知:
顶层的接口:Collection和Map
Collection接口表示存储的是单个数据组成的集合
Map接口表示存储的是key-value的键值对的集合
相关接口介绍:
Collection接口主要有三个子接口:
List接口:允许存储重复的元素的集合
Set接口:不允许存储重复的元素的集合(去重)
Queue接口:可以重复。主要是用于存储数据,模拟队列(先进先出)模拟栈(先进后出)、优先级队列
Map接口:
存储的数据是以键值对形式存储
SortMap:对于元素进行排序
实现类特点:
List接口的实现类:
ArrayList:查询速度快,线程不安全,底层基于数组来存储元素,封装一个动态的Object[]数组,是一种顺序存储的集合(元素存储是按照插入顺序有序)
LinkedList:增删速度快,线程不安全,底层是一个双向链表实现,元素也是插入有序
Vector:线程安全,查询增删速度慢,已经被ArrayList替代
Set接口的实现类:
HashSet:底层数据结构是基于HashMap实现的,不按照集合中元素的顺序,即不能保证数据和插入的顺序一致,线程不安全
LinkedHashSet:底层是基于LinkedHashMap实现的,线程不安全,能保证数据的顺序和插入顺序一致
TreeSet:基于TreeMap集合实现,线程不安全的,数据是有序的(按照属性的特征排序)
Map接口的实现类:(存储的是键值对)
HashMap:底层是一个哈希表结构,线程不安全的,允许key-value 为null,key不能重复(哈希表结构相关),数据是无序的
Hashtable:和HashMap类似,底层是哈希表接口,线程安全的,key-value不能为null
LinkedHashMap:是HashMap的子类。可以保证数据是插入有序的
TreeMap:底层是二叉树:红黑树,线程不安全的,数据可以基于元素特征排序
在线JDK文档地址:在线文档-jdk-zh
Collection接口介绍
Collection接口提供的方法:
/**
* 添加单个元素
* boolean add(E e)
* 返回值Boolean类型 true:插入成功 false:插入失败
*/
arrayList.add(23);
ArrayList <Integer> list = new ArrayList <>();
list.add(12);
list.add(15);
list.add(77);
/**
* 批量添加元素
* boolean addAll(Collection<? extends E> c)
* 返回值Boolean类型 true:插入成功 false:插入失败
*/
arrayList.addAll(list);
/**
* 删除集合中元素
* void clear()
*/
// arrayList.clear();
/**
* 判断当前集合是否存在指定的元素o
* boolean contains(Object o)
* 返回值Boolean类型 true:存在当前元素 false:不存在当前元素
*/
boolean b = arrayList.contains(12);
// System.out.println(b);
/**
* 判断集合是否为空
* boolean isEmpty()
* 返回值Boolean类型 true:没有元素 false:至少存在一个元素
*/
boolean empty = arrayList.isEmpty();
// System.out.println(empty);
/**
* 通过迭代器来遍历集合
* 迭代器是一种设计模式:作用是来遍历容器/集合而不暴露集合内部实现细节
* Iterator<E> iterator()
* 返回是Iterator类型对象
* 该类型下提供方法:
* hasNext():判断容器中是否还存在下一个元素
* next():获取当前元素
*
* 23 12 15 77
*/
Iterator <Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
System.out.print(value+" ");
}
System.out.println();
System.out.println("--------");
/**
*
* for (:)
* foreach遍历底层还是使用迭代器遍历
* 格式是:for (集合中元素类型 元素别名 : 集合实例)
*/
// for (Integer n :arrayList) {
// System.out.print(n+" ");
// }
/**
* 删除指定的元素对象o
* boolean remove(Object o)
* 返回是Boolean类型
*/
// boolean remove1 = arrayList.remove(Integer.valueOf(77));
/**
* 删除指定位置index(下标位置) 的元素
* E remove(int index)
* 返回元素类型,将删除位置的元素返回
*/
// Integer remove = arrayList.remove(1);
ArrayList <Integer> list1 = new ArrayList <>();
list1.add(15);
list1.add(77);
list1.add(33);
/**
* arrayList:23 12 15 77
* list1 :15 77 33
*/
/**
* 保存当前集合中共有的数据进行保存(求两集合的交集)
* boolean retainAll(Collection<?> c)
*
*/
// boolean b1 = arrayList.retainAll(list1);
/**
* 获取当前集合中元素的个数
* int size()
*/
int size = arrayList.size();
System.out.println(size);
ArrayList工作原理及其实现
特点、需要通过源码底层实现查看实现逻辑
JDK源码查看:JDK版本 1.7.0_80版本
ArrayList集合使用:
特点:
1、ArrayList可以存放重复数据
2、ArrayList中元素存放和插入顺序一致
3、ArrayList中存放的数据是可以为null
4、ArrayList集合底层采用的是数组来存储数据
构造函数:
//无参构造
ArrayList <Integer> list = new ArrayList <>();
//通过指定集合容量大小来实例化
ArrayList <Integer> list1 = new ArrayList <>(100);
//通过Collection集合实例来实例化一个新集合
ArrayList <Integer> list2 = new ArrayList <>(list);
通过JDK源码来研究ArrayList的实现
1、关注继承关系
2、属性和默认值
3、构造函数
4、扩容机制、扩容时机
5、底层数据结构(数组、链表、队列、栈、哈希表...)
6、常见方法实现原理(add、get、remove)
继承关系:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList继承自AbstractList,AbstractList是抽象类,实现了List结构,它是一个数组队列,提供了相关的添加、删除、修改、遍历等基本功能实现,方法子类对方法复用,如果子类有特有功能可以重写父类的方法
ArrayList实现了List接口,List接口继承自Collection接口,在Collection接口提供的方法基础上,有一些新的方法提供,比如get、set、add等特有方法
ArrayList实现了RandomAccess接口,即提供了随机访问功能,为List提供快速访问的功能
ArrayList实现了Cloneable接口,即包含了函数clone(),能被克隆
ArrayList实现了Serializable接口,意味着ArrayList支持序列化,能通过序列化去传输(IO)
属性和默认值:
//默认的初始容量10
private static final int DEFAULT_CAPACITY = 10;
//空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//存储数据:使用的是数组,数组存储的数据类型是Object
private transient Object[] elementData;
//用来记录存放数据的个数
private int size;
ArrayList底层存储元素是使用数组
数组 elementData.length:表示当前数组容量,最大存储的数据个数 size:实际存放的数据个数
构造函数:
//有参构造函数:通过指定初始容量initialCapacity来实例化ArrayList
public ArrayList(int initialCapacity) {
super();
//参数合法性校验
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
//实例化一个大小为initialCapacity数组并将数组赋值给elementData
this.elementData = new Object[initialCapacity];
}
//无参构造函数 elementData赋值为空数组
public ArrayList() {
// ArrayList(DEFAULT_CAPACITY);
super();
this.elementData = EMPTY_ELEMENTDATA;
//将默认值的赋值在add中完成
}
//通过集合来实例化ArrayList
public ArrayList(Collection<? extends E> c) {
//将集合转化为数组,直接赋值给elementData
elementData = c.toArray();
//将集合中已有元素的带下赋值给size
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
常见方法源码解析
add:添加元素
public boolean add(E e) {
//考虑扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//将数据通过数组坐标插入到数据尾部,并对size+1
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
//在添加第一个元素时,如果集合是通过无参构造实例时,会进入到if
//在未指定集合容量时,默认情况下数组初始化大小为10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//minCapacity(size+1) > elementData.length,意味着数组空间不足,扩容时机
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//扩容点
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容为原大小的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);//oldCapacity >> 1 => oldCapacity/2
//对newCapacity进行边界的校正(不能小于minCapacity,不能大于MAX_ARRAY_SIZE)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将原有的集合数据拷贝到新容量为newCapacity的数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
ArrayList在未指定容量时,初始容量时10
modCount属性是版本控制器,也是ArrayList中的属性(继承自AbstractList中属性),和业务无关,仅仅是做版本控制,在集合进行变更时(修改、删除、添加)会自增1
扩容时机和扩容机制:当要插入数据的个数大于了数组容量,需要进行扩容,将容量大小变更为原大小的1.5倍,需要将原集合数据拷贝到新数组中
get():获取元素
public E get(int index) {
//检查查询位置的合法性 index <size
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
remove:删除元素
public boolean remove(Object o) {
//删除操作判断区分是否为null值,如果为null,判断相等使用== 如果不为null,判断相等使用equals
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//删除操作
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
//将index位置之后的数据全部前移一位
System.arraycopy(elementData, index+1, elementData, index,numMoved);
//Arrays.copyof() 拷贝和System.arraycopy 异同点、效率????
//将最后一个位置置为null,并将size-1
elementData[--size] = null; // clear to let GC do its work
}