List接口:有序 允许重复
public interface List<E> extends Collection<E>
继承自Collection接口的方法:
- boolean add(E e);向集合末尾追加元素e对象
- boolean remove(Object obj)删除第一个和obj相等的元素,
如果没有和obj相等元素,则报异常IndexOutOfBoundsException
List接口中的特殊方法
- void add(int index, E element); 向指定索引位置index上添加元素element,
原始数据自动后移
- E get(int index); 获取指定索引号对应的元素,index应该在[0,size-1]
- E set(int index, E element); 用于给指定索引位置上进行赋值,这个位
置上必须有对应的数据(已经赋过值),这里实际上是修改操作,否则
IndexOutOfBoundsException
- E remove(int index);删除指定位置的元素,可以返回原始位置上存储的元素
- int indexOf(Object o); 查找从左向右第一个o元素的下标索引,如果元素
不存在返回-1
- int lastIndexOf(Object o);从右向左查找
对象相等判定使用的是equals方法:
- sort方法按照自定义比较器对集合中的所有元素进行排序,默认自然序
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray(); //将List集合对象转换为数组
Arrays.sort(a, (Comparator) c); //调用Arrays工具类中的排序方法对数组进行排序
ListIterator<E> i = this.listIterator(); //获取List集合对象中特殊的Iterator迭代器对象
for (Object e : a) { //foreach结构遍历数组中的所有元素
i.next();
i.set((E) e); //修改集合List中迭代器指定的当前位置上的元素
}
}
list.sort(new Comparator<A2>() {
public int compare(A2 o1, A2 o2) {
// 自定义比较规则,按照id从小到大,如果id相等则按照name从大到小
int res = o1.getId().compareTo(o2.getId());
if (res == 0)
res = (o1.getName().compareTo(o2.getName())) * -1;
return res;
}
});
对应的实现类:
ArrayList底层实现为数组,线程不安全
Vector底层实现为数组,线程安全 synchronized
LinkedList底层实现为链表,线程不安全
Set集合:无序,不允许重复
public interface Set<E> extends Collection<E>
没有新方法
- boolean add(E e);向集合中追加元素e对象,如果出现重复则后添加数
据直接丢弃
如果进行对象相等比较:
- 首先调用当前对象所属类中的hashCode方法获取当前对象的hashCode值
- 按照hashCode值进行比较,
- 如果hashCode值不相等,则不会调用equals方法,直接得出结论两
个对象不相等
- 如果hashCode值相等,才调用equals方法进行进一步判断
- 如果equals为真则判断两个对象相等
潜规则:
java要求当两个对象的equals为true时,要求两个对象的hashCode值相等。
hashCode值相等并不一定equals为true
实现类:
- HashSet
- TreeSet
- LinkedHashSet
ArrayList的使用和实现:
List list=new ArrayList();
public class ArrayList<E>
extends AbstractList<E> 通过继承抽象类可以共享所有公共方法
implements List<E>, 实现List接口
RandomAccess, 实现随机访问接口
Cloneable, 实现克隆接口
java.io.Serializable 实现序列化接口
transient Object[] elementData; 真是存放数据的数组
private int size; 当前集合中存储的元素个数
构造器
public ArrayList() {
//针对存储数据的数组进行初始化操作
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
//常量定义为DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};空数组
//如果使用无参构造器,则不会直接创建数组,而是采用空数组,当第一次添加元素时才创建数组
//使用无参构造器时ArrayList会构建一个空数组用于未来存放数据,这里是一种对内存消耗的优化处理
}
带参构造器 List list=new ArrayList(18)
18就是初始化容积,这里的初始化参数值必须为[0,int的最大值)
public ArrayList(int initialCapacity) { //参数为初始化容积
if (initialCapacity > 0) { 如果初始化容积大于0,则按照指定的容积创建数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA; 空数组,长度为0的数组
} else { //初始化容积值小于0则报异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
add方法的定义[重点]:
protected transient int modCount = 0;
public boolean add(E e) {
modCount++; 用于统计当前集合对象的修改次数
add(e, elementData, size); 参数1:新增元素;参数2:存储数据的数组;参数3:当前集合中存储的元素个数
return true; 返回true表示添加成功
}
private void add(E e, Object[] elementData, int s) {
//如果数组已经存满了,则首先进行扩容处理
if (s == elementData.length) 判断当前集合中存储的元素个数是否和数组长度相等
elementData = grow(); 如果相等则进行扩容处理
elementData[s] = e; 在数组的指定位置存储元素
size = s + 1; 集合中所存储的元素个数
}
private Object[] grow() { //扩容处理方法
return grow(size + 1); //继续调用其它扩容方法,参数为 当前存储个元素个数+1---最小容积
}
private Object[] grow(int minCapacity) {
//调用Arrays工具类中的方法进行数组的拷贝,同时调用newCapacity方法计算所需要的新容积值
return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));
}
private static final int DEFAULT_CAPACITY = 10;
private int newCapacity(int minCapacity) { 计算所需要的新容积值
int oldCapacity = elementData.length; 计算原来数组的长度
//计算新容积值
int newCapacity = oldCapacity + (oldCapacity >> 1); 相当于是老容积增加50%,这就是扩容比
//计算出的新容积值是否满足所需要的最小容积值
if (newCapacity - minCapacity <= 0) {
//如果存储数据的数组为初始化时的空数组,则计算默认初始化容积10和所需要最小容积值则最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // OOM内存溢出
throw new OutOfMemoryError();
return minCapacity;
}
//如果计算出的新容积值大于所需要的最小容积值
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
//如果计算出的新容积值小于最大允许的容积值,则返回计算出的新容积
(minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
//如果计算出的新容积值大于最大允许的容积值并且minCapacity大于MAX_ARRAY_SIZE,则返回最大整数值,否则返回最大允许的容积值
}
//数组长度的上限值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
数组的拷贝操纵定义在Arrays工具类中,copyof会新建一个指定长度newLength的数组,并且将原始数组中的所有元素拷贝到新数组中,同时返回新创建的数组
* 参数1是原始数组,参数2为所需要的新长度
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
The maximum size of array to allocate (unless necessary).
Some VMs reserve some header words in an array.
Attempts to allocate larger arrays may result in
OutOfMemoryError: Requested array size exceeds VM limit
要分配的最大数组大小(除非必要)。一些虚拟机在数组中保留一些头字。尝试分配较大的阵列可能会导致:OutOfMemoryError请求的数组大小超过VM限制
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
由于在不同的平台上,受到平台的影响导致能够为数组分配的实际最大数值并非
为Integer.MAX_VALUE(2,147,483,647),而是与这个值相接近的数值。因此,
作者减8实际上是因为不想让你创建的数组在扩容时计算的新容量值等于或过于
接近最大值不能被平台分配出来而报出OOM错误。
结论:
- 如果直接初始ArrayList,则采用延迟初始化处理【初始化数组长度为0】的 方式以节约内存,当添加数据时,才进行数组的初始化,默认初始化容积为10
- 添加数据时当存储数据的数组长度不足时,数组会自动变长,变长的比例为 原容积的50%
删除元素:
public E remove(int index) {
Objects.checkIndex(index, size); //检查所需要删除位置下标参数是否在合理的范围内[0,size-1],如果超出范围则报异常
final Object[] es = elementData; 获取所存储的所有元素数组
E oldValue = (E) es[index]; 获取指定位置上的元素
fastRemove(es, index); 利用System.arrayCopy的方法使用覆盖的方式删除指定位置上的元素
return oldValue; 返回删除掉的元素
//没有缩容处理
}
private void fastRemove(Object[] es, int i) {
modCount++; 修改次数+1
final int newSize;
if ((newSize = size - 1) > i) //size - 1就是可以删除元素的最大下标
System.arraycopy(es, i + 1, es, i, newSize - i); 通过数组拷贝的方式覆盖掉指定位置的元素
es[size = newSize] = null; //最后位置赋null值
{1,2,3,4,5} 执行arrayCopy方法需要覆盖下标为2的元素{1,2,4,5,5}
}
结论:
- 使用数组元素移动的方式实现元素的删除。注意:这里没有变小容积
- 修改元素个数时会有modCount的修改--快速失败
get方法的实现:
public E get(int index) {
Objects.checkIndex(index, size);针对索引下标进行合法性验证
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
迭代器的实现类中定义【内部类】
private void checkForComodification() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException(); 并发修改异常
}
}
结论:首先要求index应该在[0,size-1]的范围内,否则异常
如果index正确则按照下标从数组中获取元素
如果多个线程同时操作一个ArrayList,可以通过ConcurrentModificationException解决修改出现的线程安全问题