数据结构之动态数组
标签:
学点算法
数组是一种顺序存储的线性表,所有元素的内存地址是连续的。在很多编程语言中数组都无法动态修改容量,我们今天就简单实现一下可以动态改变容量的数组。
首先实现一个实体类来作为操作对象
public class Person {
private int age;
private String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//重写 toString 方法用来打印
@Override
public String toString() {
return "Person {age=" + age + ", name=" + name + "}";
}
//重写 finalize 方法用来监控销毁状态
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
super.finalize();
System.out.println("我被销毁了");
}
//重写 equals 方法用来比较对象
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
//如果是空或者不是 Person 对象
if(obj == null || !(obj instanceof Person)) return false;
Person person = (Person)obj;
return (this.age == person.age && this.name.equals(person.name));
}
}
实现动态数组
其实也就是 增删改查
需要注意的几点
-
1.)注意判断范围 一般情况下判断的范围都是不小于0,并且小于最大元素数(因为是已经存在的元素,所以范围是 0 到 size - 1,所以不能等于 size);但是在某个位置添加的时候范围可以等于最大元素数(因为是不存在的,插入范围是 0 到 size)。
/** * 判断范围 * @param index */ private void rangeCheck(int index) { if(index < 0 || index >= size) { outOfBounds(index); } } /** * 判断添加的范围 * @param index */ private void rangeCheckAdd(int index) { if(index < 0 || index > size) { outOfBounds(index); } }
-
2.)注意添加/删除某个位置元素时的挪动顺序 添加的时候先挪动索引最大的元素; 删除的时候先挪动索引最小的元素。
-
3.)注意扩容 就是新创建一个大空间的数组,将老数组中的元素转移过去
-
4.)移除的时候最后一个位置要清空 如果最后一个位置不设置为 null 的话会导致挪动之后最后一个位置所指向的对象无法销毁; 还有先减 size 再清空最后一个元素,毕竟是从 0 开始记的。
- 5.)清空的时候需要将元素置空 存储的是对象的话其实是存储的指向堆空间的地址,所以要将元素对象置空,可以清除内存空间/
最终代码
public class ArrayList<E> {
private int size = 0;
private E[] elements;
private static final int DEFAULT_CAPATICY= 10;
private static final int ELEMENT_NOT_FOUND= -1;
//构造方法
public ArrayList(int capaticy) {
capaticy = (capaticy < DEFAULT_CAPATICY) ? DEFAULT_CAPATICY : capaticy;
elements = (E[])new Object[capaticy];
}
//构造方法
public ArrayList() {
this(DEFAULT_CAPATICY);
}
public int size() {
return size;
}
/**
* @return
*/
public boolean isEmpty() {
return size == 0;
}
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUND;
}
public void add(E element) {
add(size, element);
}
public E get(int index) {
rangeCheck(index);
return elements[index];
}
public E set(int index, E element) {
rangeCheck(index);
E old = elements[index];
elements[index] = element;
return old;
}
public void add(int index, E element) {
//注意范围
CheckAdd(index);
//保证容量(扩容)
ensureCapacity(size);
// for (int i = size - 1; i >= index; i--) {
// elements[i + 1] = elements[i];
//改进,减少计算
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = element;
size ++;
}
//移除某个位置的元素
public E remove(int index) {
rangeCheck(index);
E old = elements[index];
//
// for (int i = index + 1; i <= size - 1; i++) {
//改进,减少计算
for (int i = index + 1; i < size; i++) {
elements[i - 1] = elements[i];
}
size --;
//把最后一个元素清空,思考
elements[size] = null;
return old;
}
//移除元素
public void remove(E element) {
remove(indexOf(element));
}
//这里注意对象的比较
public int indexOf(E element) {
if(element == null) {
for (int i = 0; i < size; i++) {
//如果查找的对象是空,找到第一个为空的对象返回索引
if (elements[i] == null) return i;
}
} else {
for (int i = 0; i < size; i++) {
if(element.equals(elements[i])) {
return i;
}
}
}
return ELEMENT_NOT_FOUND;
}
public void clear() {
///如果存对象就不能简简单单的把size置为0了
for (int i = 0; i < size; i++) {
//销毁数组里的对象
elements[i] = null;
}
size = 0;
}
/**
* 抛出异常
* @param index
*/
private void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
/**
* 判断范围
* @param index
*/
private void rangeCheck(int index) {
if(index < 0 || index >= size) {
outOfBounds(index);
}
}
/**
* 判断范围
* @param index
*/
private void rangeCheckAdd(int index) {
if(index < 0 || index > size) {
outOfBounds(index);
}
}
/**
* 扩容
* @param capacity
*/
private void ensureCapacity(int capacity) {
int oldCapacity = elements.length;
if(oldCapacity >= capacity) {
return;
}
int newCapacity = oldCapacity + oldCapacity >> 1; // 1 + 1/2 个老的空间,相当于乘以1.5倍,这样写效率高于直接乘以 1.5 的浮点运算
E[] newElements = (E[])new Object[newCapacity];
//把老的元素转移到心的数组中
for (int i = 0; i < elements.length; i++) {
newElements[i] = elements[i];
}
elements = newElements;
}
//重写 toString 用于打印
@Override
public String toString() {
//对于字符串拼接来说更有效率
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Size=").append(size).append(", [");
for (int i = 0; i < size; i++) {
if(i !=0 ) {
stringBuilder.append(", ");
}
stringBuilder.append(elements[i].toString());
}
stringBuilder.append("]");
return stringBuilder.toString();
}
}