手动实现Arraylist的重要方法
1.简单实现初始化,toString(),add()方法
public class Arraylist01 {
//标志位,标志数数组下一次增加新元素的下标
private int size;
// 数组
private Object [] elementDate;
// 设置初始长度10
private static final int DEFALT_CAPACITY = 10;
// 无参构造器里设置初始长度
public Arraylist01() {
elementDate = new Object[DEFALT_CAPACITY];
}
public Arraylist01(int capacity) {
elementDate = new Object[capacity];
}
// 添加元素
public void add(Object obj) {
elementDate[size++] = obj;
}
}
// 重写toString
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < size; i++) {
sb.append(elementDate[i]+",");
}
// 修改最后一个字符','
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
2.增加泛型,让操作更方便,可以略过
public class Arraylist2<E> {
// 标志位,标志数数组下一次增加新元素的下标
private int size;
// 数组
private Object [] elementDate;
// 设置初始长度10
private static final int DEFALT_CAPACITY = 10;
// 无参构造器里设置初始长度
public Arraylist2() {
elementDate = new Object[DEFALT_CAPACITY];
}
public Arraylist2(int capacity) {
elementDate = new Object[capacity];
}
// 添加元素
public void add(E e) {
elementDate[size++] = e;
}
// 重写toString
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < size; i++) {
sb.append(elementDate[i]+",");
}
// 修改最后一个字符','
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
}
3.完善add()方法,增加扩容机制
重点是什么时候用扩容,扩容的拷贝实现
数组复制方法:
拷贝操作(老数组,从哪里开始拷贝,新数组,从哪里开始粘贴,拷贝多少个)
System.arraycopy(elementDate, 0, newArray, 0, elementDate.length);
// 添加元素
public void add(E e) {
// 什么时候扩容
if (size == elementDate.length) {
// 扩容操作,构建新数组,注意右移带括号,运算优先级问题 20-->20+20/2;
// 右移是底层运算(位运算),比乘除运算效率高
Object [] newArray = new Object[elementDate.length+(elementDate.length>>1)];
// 拷贝操作 (老数组,从哪里开始拷贝,新数组,从哪里开始粘贴,拷贝多少个)
System.arraycopy(elementDate, 0, newArray, 0, elementDate.length);
// 赋给老数组
elementDate = newArray;
// 最后进行添加元素
}
// 扩容与不扩容都需要添加新元素
elementDate[size++] = e;
}
4.完善有参构造
// 有参构造,判断传入参数合法
public Arraylist4(int capacity) {
if (capacity < 0) {
throw new RuntimeException("初始容量有误:"+capacity);
}else if (capacity == 0) {
// 当为0时,自动设置为20
elementDate = new Object[DEFALT_CAPACITY];
}else {
elementDate = new Object[capacity];
}
}
5.增加get(),set(),索引方法
// 根据下标取出元素
public E get(int index) {
checkRange(index);
return (E) elementDate[index];
}
// 根据下标,插入
public void set(int index,E e) {
checkRange(index);
elementDate[index] = e;
}
// 判断索引是否有效 ,无效直接抛出异常
public void checkRange(int index) {
// size 标志下一次的位置 ,[0,index),超出范围抛出异常
if (index < 0 || index >= size) {
throw new RuntimeException("索引错误:"+index);
}
}
6.增加remove(int),remove(object)两个移除
// 根据元素移除,明白从前往后查找相同的,删除第一个
public boolean remove(E e) {
// 遍历数组,找到元素
for (int i = 0; i < size; i++) {
// 容器里的比较操作 用equals,而不是==
if (get(i).equals(e)) {
// 调用根据下标移除方法
remove(i);
return true;
}
}
return false;
}
// 根据下标移除
public E remove(int index) {
// 假设移除f(小标为3),
// a,s,d,f,g,h,j,k
// a,s,d,g,h,j,k,k
// a,s,d,g,h,j,k
// 检验下标合法
checkRange(index);
// 根据size 算出应该copy几个元素
int numMove = size-index-1;
// 在删除元素不为最后一个时,才需要copy操作,下面两个意义相同
// if (index != size) {
if (numMove > 0 ) {
System.arraycopy(elementDate, index+1, elementDate, index,numMove );
}
// 改变size标志位,将最后一位置空(虽然未置空不会影响操作,但是有利于GC)
elementDate[--size] = null;
return (E) elementDate[index];
}
7.增加size(),isEmpty()
public int size() {
return size;
}
public boolean isEmpty() {
return size==0 ? true:false;
}
8完整代码
public class Arraylist5<E> {
// 标志位,标志数数组下一次增加新元素的下标
private int size;
// 数组
private Object [] elementDate;
// 设置初始长度10
private static final int DEFALT_CAPACITY = 10;
// 无参构造器里设置初始长度
public Arraylist5() {
elementDate = new Object[DEFALT_CAPACITY];
}
// 有参构造,判断传入参数合法
public Arraylist5(int capacity) {
if (capacity < 0) {
throw new RuntimeException("初始容量有误:"+capacity);
}else if (capacity == 0) {
// 当为0时,自动设置为20
elementDate = new Object[DEFALT_CAPACITY];
}else {
elementDate = new Object[capacity];
}
}
public int size() {
return size;
}
public boolean isEmpty() {
return size==0 ? true:false;
}
// 添加元素
public void add(E e) {
// 什么时候扩容
if (size == elementDate.length) {
// 扩容操作,构建新数组,注意右移带括号 20-->20+20/2;
Object [] newArray = new Object[elementDate.length+(elementDate.length>>1)];
// 拷贝操作 (老数组,从哪里开始拷贝,新数组,从哪里开始粘贴,拷贝多少个)
System.arraycopy(elementDate, 0, newArray, 0, elementDate.length);
// 赋给老数组
elementDate = newArray;
// 最后进行添加元素
}
elementDate[size++] = e;
}
// 根据下标取出元素
public E get(int index) {
checkRange(index);
return (E) elementDate[index];
}
// 根据下标,插入
public void set(int index,E e) {
checkRange(index);
elementDate[index] = e;
}
// 判断索引是否有效 ,无效直接抛出异常
private void checkRange(int index) {
// size 标志下一次的位置 ,[0,index),超出范围抛出异常
if (index < 0 || index >= size) {
throw new RuntimeException("索引错误:"+index);
}
}
// 根据元素移除,明白从前往后查找相同的,删除第一个
public boolean remove(E e) {
// 遍历数组,找到元素
for (int i = 0; i < size; i++) {
// 容器里的比较操作 用equals,而不是==
if (get(i).equals(e)) {
// 调用根据下标移除方法
remove(i);
return true;
}
}
return false;
}
// 根据下标移除
public E remove(int index) {
// 假设移除f(小标为3),
// a,s,d,f,g,h,j,k
// a,s,d,g,h,j,k,k
// a,s,d,g,h,j,k
// 检验下标合法
checkRange(index);
// 根据size 算出应该copy几个元素
int numMove = size-index-1;
// 在删除元素不为最后一个时,才需要copy操作,下面两个意义相同
// if (index != size) {
if (numMove > 0 ) {
System.arraycopy(elementDate, index+1, elementDate, index,numMove );
}
// 改变size标志位,将最后一位置空(虽然未置空不会影响操作,但是有利于GC)
elementDate[--size] = null;
return (E) elementDate[index];
}
// 重写toString
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < size; i++) {
sb.append(elementDate[i]+",");
}
// 修改最后一个字符','
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
}
9.总结
这是第一次手动实现一个集合的重要方法,它的意义在于明白Arraylist集合的底层大概实现,以及对之前知识的一个熟练运用,可能有不完善的地方,望大家多多提出,理解!😁