- List集合有序集合,有序有重复,访问集合中的元素可以根据元素的索引来访问
- Set集合是无序集合,集合中的元素无序无重复,访问集合中的元素只能根据元素本身访问(也是集合元素不允许重复的原因)
- Map集合用key来访问value
- <<和>>
其中<< 表示的是向左移位,>> 表示向位移
(1) << : 左位移运算符,num<<1,相当于num*2
(2) >>: 右位移运算符,num>>1,相当于num/2
- 该API可以把原来的数组复制一份,并扩展成新的数组的长度,返回是新的数组
- 只会扩充长度,数组的元素并没有改变,原来是几个元素现在还是几个,只不过现在的数组的长度变大了
java代码示例:
/**
* 数组的动态扩容技术
* 1. copyof
*/
public class Test001 {
public static void main(String[] args) {
copyOfArray1();
}
private static void copyOfArray1() {
// 1. 定义数组长度是2
Object[] objects = {1, 2};
System.out.println("原数组长度: " + objects.length);
// 2. 数组扩容,将objects数组的长度扩大到10,返回一个新的数组,元素与objects一致,长度改变
Object[] newObjects = Arrays.copyOf(objects, 10);
System.out.println("新数组长度: " + newObjects.length);
}
}
结果如下:
原数组长度: 2
新数组长度: 10
Process finished with exit code 0
- 该函数的作用是拷贝数组。
- src:原数组
- srcPos:起始的位置
- dest:目标数组
- destPos:目标数组的起始位置
- length:复制的长度
- 注意:
(1)src和dest都必须是同类型或者可以进行转换类型的数组。该函数也可以实现自己复制自己。
(2)复制的目标数组待复制的元素长度不要超过目标数组的长度
举例说明int[] fun = {0,1,2,3,4,5,6};
System.arraycopy(fun, 0 , fun,3,3)
结果是{0,1,2,0,1,2,6}实现过程是:先生成了一个长度是length的临时数组,将fun数组中srcPos到srcPos+length-1之间的数据拷贝到临时数组中,再执行System.arraycopy(临时数组,0,fun,目标数组的起始位置,临时数组的长度即length);
小结一下:
虽然两种方法都可以实现数组扩容,但是如果数组的长度比较大,那么使用System.arraycopy会比较有优势,因为其使用的是内存复制,省区了大量的数组寻址访问等时间。
(1)List是存在有参和无参两种构造的
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
我们可以发现无参构造,我们默认为其创建了一个空的数组,并没有规定其长度
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
我们发现其有参构造当我们的参数传递一个大于0的数字的时候,我们会创建指定长度的数组;如果长度等于0,创建一个空的数组。注意:这里的指定长度若大于0,则初始化数组的时候会指定数组的长度,其他情况不会指定数组的长度
(2)list的add方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
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);
}
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);
}
我们可以看到上面的代码:
(1)在add方法的时候会判断是否需要扩容操作,传入的是当前正在添加第几个元素(没有第0个),也就是我们内存扩容的最小大小,最小就应该是数组可以放进去当前的元素。
(2)如果当前要扩容的最小大小大于了数组的长度,则应该对其进行扩容操作。
(3)扩容操作的扩容长度应该是原来数组大小的1.5倍
(4)当扩容大小小于最小的扩容长度,则取最小扩容长度为新数组的长度。
(5)对数组进行扩容
(6)扩容完成后,将新添加的元素放到数组中
(3) list的删除操作(remove操作,根据下标删除,返回删除的元素)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
其中elementData是数组,index代表了要被删除的元素的下标,arraycopy(原数组,原数组起始位置,目标数组,目标数组起始位置,复制的长度)
(4)list的删除操作(根据元素删除)
先根据对象查找出在数组中的具体的位置
再根据以下标进行删除做同样的操作
注意:我们删除元素的时候只可以删除第一个出现的,即使有多个存在,也不会全部删除
JAVA的手写ArrayList,我们这里只写其核心方法的实现:
(1)List接口手写
package com.xiyou.myList;
/**
* 抽象接口,相当于是List
* @author 郑一帆
*/
public interface ExList<E> {
/**
* 添加元素
* @param object
*/
public void add(E object);
/**
* 在指定的位置上添加元素
* @param index
* @param object
*/
public void add(int index, E object);
/**
* 根据下标移除元素
* @param index
* @return
*/
public E remove(int index);
/**
* 移除元素,但是只能移除一个,即使有重复的
* @param object
* @return
*/
public boolean remove(E object);
/**
* 返回列表的长度
* @return
*/
public int getSize();
/**
* 根据下标值取出具体的元素
* @param index
* @return
*/
public E get(int index);
}
(2)ArrayList
package com.xiyou.myList;
import java.util.Arrays;
/**
* 纯手写ArrayList
* @author zyf
*/
public class ExtArrayList<E> implements ExList<E>{
/**
* 用来保存列表的底层实现,数组实现列表
*/
private transient Object[] elementData;
/**
* 保存列表中实际存储的值
*/
private int size;
/**
* 无参构造
*/
public ExtArrayList(){
// 默认创建一个初始值为10的
this(10);
}
/**
* 有参构造,传入的参数表示的是初始化的数组的长度
* @param initialCapacity
*/
public ExtArrayList(int initialCapacity) {
if (initialCapacity < 0) {
// 如果构建数组的长度小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
// 创建初始化大小的数组
elementData = new Object[initialCapacity];
}
/**
* 添加一个元素到列表的最后
* @param object
*/
@Override
public void add(E object) {
// 判断是否需要扩容
// 传入的是当前正在准备处理的第几个元素
ensureExplicitCapacity(size+1);
// 保存到数组中
elementData[size++] = object;
}
/**
* 判断数组是否足够大 是否需要扩容
* @param minCapacity 最小的扩容大小,传进来的是准备要处理的第几个元素,我们数组最起码应该有这么大,能存放进去
*/
private void ensureExplicitCapacity(int minCapacity) {
// 判断当前存储的元素是否等于数组的大小,等于的话则无法进行新的元素的存储,需要扩容
if (size == elementData.length) {
// 获取原来数组长度
int oldCapacity = elementData.length;
// 扩容成原来的1.5倍 >>1 相当于/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 最新的扩容的大小小于最小的扩容大小
if (newCapacity < minCapacity) {
// 扩容大小变为最小的扩容大小
newCapacity = minCapacity;
}
// 进行扩容, 将数组elementData扩大到长度是newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
/**
* 在指定的位置上添加元素
* @param index
* @param object
*/
@Override
public void add(int index, E object) {
// 检查下标是否越界
rangeCheck(index);
// 检查是否需要扩容
ensureExplicitCapacity(size + 1);
// 进行扩容
System.arraycopy(elementData, index, elementData, index + 1, size - index);
// 已经复制过了,然后将index上面的元素进行替换
elementData[index] = object;
size ++ ;
}
/**
* 判断数组是否越界
* @param index
*/
private void rangeCheck(int index) {
// 这里不能和数组的长度比较,因为数组长度会进行扩容,存了多少并不是数组的长度
if (index >= size) {
throw new IndexOutOfBoundsException("数组越界");
}
}
/**
* 根据下标值进行删除
* @param index
* @return
*/
@Override
public E remove(int index) {
// 得到删除的元素,一会要返回
E object = get(index);
// 应该复制多少
int numMoved = size - index -1;
if (numMoved > 0 ) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
// 把最后一位赋值为空
elementData[--size] = null;
return object;
}
/**
* 根据元素进行删除
* @param object
* @return
*/
@Override
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;
}
@Override
public int getSize() {
return size;
}
@Override
public E get(int index) {
// 检查数组下标是否越界
rangeCheck(index);
return (E) elementData[index];
}
}
Vector的集合实现和ArrayList的实现一模一样,只不过是在扩容的时候加了synchronized(扩容是在add方法中实现的,所以也是锁是加在add方法上的)
(1)Vector是线程安全的,但是性能要比ArrayList低
(2)ArrayList和Vector都是采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加原来的50%,Vector默认增加原来的一倍
(3)Vector可以设置capacityIncrement(扩容时候增大多少),而ArrayList不行,从字面的意思理解就是capacity容量,Increment增加,容量增长的参数。