源代码:https://gitee.com/AgentXiao/Collection
10月7日,国庆假期即将过去,秋招又一次高潮即将来临,为了更好地准备,复习一下容器的使用。
一、List
List的三个常用子类:
1、ArrayList:底层实现是数组,线程不安全,效率高;查询快,修改、插入、删除慢。
2、LinkedList:底层实现是链表,线程不安全,效率高。查询慢,修改、插入、删除快。
3、Vector:线程安全的,效率低。
二、自己实现一个MyArrayList
1、首先明确,ArrayList的底层是一个Object[],用int size作为计量容器中对象个数的变量。
private Object[] elementDate; //对象数组
private int size; //容器中的对象个数
2、构造器:当实例化一个ArrayList时,默认创建一个容量为10的Object数组。需要注意,构造器应该是两个,一个有参一个无参,无参默认为10,有参可以指定容量大小。
//无参构造,默认容量为0
public MyArrayList() {
this(10);
}
//有参构造,指定容量
public MyArrayList(int initialCapacity) {
if(initialCapacity > 0){
elementDate = new Object[initialCapacity];
}else if(initialCapacity == 0){
elementDate= new Object[]{};
}else{
throw new RuntimeException("不能构造容量小于0的容器!");
}
}
3、增加方法add(Object obj):向容器中增加一个对象。首先需要考虑的问题是,空间是否足够?
如果空间足够,直接赋值即可;如果空间不够,则需要进行扩容ensureCapacity。扩容的三个步骤:扩容 -> 拷贝 -> 替换
//确保容量足够
private void ensureCapacity(){
//如果数组内的对象已达到最大值
if(size == elementDate.length){
//扩容:size*2+1
Object[] newElementDate = new Object[size*2+1];
//拷贝:将旧数组的内容拷贝到新数组
System.arraycopy(elementDate,0,newElementDate,0,elementDate.length);
//替换:旧数组替换为新数组
elementDate = newElementDate;
}
}
/**
* @MethodName add
* @Descrition 添加对象
* @Param [obj]
* @return void
*/
public void add(Object obj){
ensureCapacity();
elementDate[size++] = obj;
}
4、获取长度size()
/**
* @MethodName size
* @Descrition 返回数组中对象的个数
* @Param []
* @return int
*/
public int size(){
return size;
}
5、isEmpty()
/**
* @MethodName isEmpty
* @Descrition 判断数组中对象个数是否为0
* @Param []
* @return boolean
*/
public boolean isEmpty(){
return size == 0;
}
6、增加方法2:add(int index,Object obj)。在指定的索引位置插入指定的对象
(1)首先考虑索引值是否符合输入规范,封装一个范围检测方法
/**
* @MethodName rangeCheck
* @Descrition 索引检测
* @Param [index]
* @return void
*/
private void rangeCheck(int index){
if(index >= size){
throw new RuntimeException("你输入的索引值不在范围之内!");
}
}
(2)其次判断是否需要扩容ensureCapacity()
(3)最后进行插入(拷贝 -> 赋值)特别需要注意移动的位数
/**
* @MethodName add
* @Descrition 在指定的位置插入对象
* @Param [index, obj]
* @return void
*/
public void add(int index,Object obj){
rangeCheck(index);
ensureCapacity();
//拷贝:相当于集体往后移动
System.arraycopy(elementDate,index,elementDate,index+1,size-index);
//赋值
elementDate[index] = obj;
size++;
}
7、获取对象方法get(int index)
(1)索引值判断
(2)获取
/**
* @MethodName get
* @Descrition 获取指定索引对象
* @Param [index]
* @return java.lang.Object
*/
public Object get(int index){
rangeCheck(index);
return elementDate[index];
}
8、移除方法1:remove(int index)
(1)拷贝(往前移)
(2)将最后一个对象赋值为null
一定要搞清楚变量的变化,最好举个例子进行模拟
/**
* @MethodName remove
* @Descrition 移除指定索引位置的对象
* @Param [index]
* @return void
*/
public void remove(int index){
rangeCheck(index);
//拷贝(往前移)
int numMoved = size - index - 1;
if(numMoved > 0){
System.arraycopy(elementDate,index+1,elementDate,index,numMoved);
}
//赋值
size--;
elementDate[size] = null;
}
9、移除方法2:remove(Object obj)
(1)这里需要特别注意的是,底层使用equals判断是那个对象
/**
* @MethodName remove
* @Descrition 移除指定的对象
* @Param [obj]
* @return void
*/
public void remove(Object obj){
for(int i=0;i<size;i++){
if(elementDate[i].equals(obj)){ //注意:底层使用的equals
remove(i);
}
}
}
10、替换方法set(int index,Object obj)
(1)判断所引致规范
(2)替换
/**
* @MethodName set
* @Descrition 替换指定位置的对象
* @Param [index, obj]
* @return java.lang.Object
*/
public Object set(int index,Object obj){
rangeCheck(index);
Object oldElement = elementDate[index];
elementDate[index] = obj;
return oldElement;
}