文章目录
什么是数据结构
数据结构是计算机存储、组织数据的方式。
在实际应用中,根据使用场景来选择最合适的数据结构。
线性表
线性表是具有 n 个相同类型元素的有限序列( n ≥ 0 )
- a1 是首节点(首元素),an 是尾结点(尾元素)
- a1 是 a2 的前驱, a2 是 a1 的后继
常见的线性表有:
- 数组
- 链表
- 栈
- 队列
- 哈希表 (散列表)
数组(Array)
数组是一种顺序存储的线性表,所有元素的内存地址是连续的。
在很多编程语言中,数组都有个致命的缺点:无法动态修改容量
实际开发中,我们更希望数组的容量是可以动态改变的
动态数组(Dynamic Array)接口设计
动态数组的设计
在Java中,成员变量会自动初始化,比如
- int 类型自动初始化为 0
- 对象类型自动初始化为 null
除上述接口外还需要设置以下属性和方法:
// 元素的数量
private int size;
// 所有元素
private E[] elements;
// 数组默认容量大小
private static final int DEFAULT_CAPACITY = 10;
// 按索引查找时没有找到数据就返回-1
private static final int ELEMENT_NOT_FOUND = -1;
// 为数组开辟空间
public ArrayList(int capacity){
capacity = (capacity < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;
elements = (E[]) new Object[capacity];
}
public ArrayList(){
this(DEFAULT_CAPACITY);
}
// 数组下标越界后手动抛出异常
private void outOfBound(int index){
throw new ArrayIndexOutOfBoundsException("Index:" + index + ",Size:" + size);
}
// 检测数组下标是否越界
private void rangeCheck(int index){
if (index < 0 || index >= size){
outOfBound(index);
}
}
// 特殊情况:检测数组添加元素时下标是否越界
private void rangeCheckForAdd(int index){
if (index < 0 || index > size){
outOfBound(index);
}
}
添加元素 - add(E element)
add(E element)
:将元素添加到最后面
add(int index, E element)
:将元素添加到指定位置
代码实现:
/**
* 往index位置添加元素
* @param index
* @param element
*/
public void add(int index, E element){
rangeCheckForAdd(index); // 检查数组下标是否越界
ensureCapacity(size + 1); // 扩容,确保容量够大
for (int i = size - 1; i >= index; i--) {
elements[i+1] = elements[i];
}
elements[index] = element;
size++;
}
/**
* 添加元素到最后面
* @param element
*/
public void add(E element){
add(size, element);
}
清除所有元素 - clear()
- 若为基本数据类型,设置
size = 0
即可,不需要设置elements = null
- 使用泛型后要需要将元素置为null
代码实现:
/**
* 清除所有元素
*/
public void clear(){
// 使用泛型后要注意内存管理,要将元素置为null
for (int i = 0; i < size; i++) {
elements[i] = null;
}
size = 0;
}
删除元素 - remove(int index)
最后一个元素需要如何处理:
- 如果存放 int 类型,size-- 后,最后一个元素已经无法访问了。
- 使用泛型后要注意内存管理,要将元素置为null。
代码实现:
/**
* 删除index位置对应的元素
* @param index
* @return
*/
public E remove(int index){
rangeCheck(index);
E old = elements[index];
for (int i = index + 1; i <= size - 1; i++) {
elements[i -1] = elements[i];
}
elements[--size] = null;
return old;
}
打印数组
- 重写 toString 方法
- 在 toString 方法中将元素拼接成字符串
- 字符串拼接建议使用 StringBuilder
代码实现:
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("size = ").append(size).append(", [");
for (int i = 0; i < size; i++) {
if (i != 0) {
stringBuffer.append(", ");
}
stringBuffer.append(elements[i]);
}
stringBuffer.append("]");
return stringBuffer.toString();
}
查找元素
/**
* 是否包含某个元素
* @param element
* @return
*/
public boolean contains(E element){
return indexOf(element) != ELEMENT_NOT_FOUND;
}
/**
* 返回index位置对应的元素
* @param index
* @return
*/
public E get(int index){
rangeCheck(index);
return elements[index];
}
/**
* 查看元素的位置
* @param element
* @return
*/
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;
}
扩容
代码实现:
// 扩容操作
private void ensureCapacity(int capacity){
int oldCapacity = elements.length;
if(oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
}
对象数组
Object数组中存放的是对象的引用地址
泛型动态数组源码(Java版)
public class ArrayList<E> {
// 元素的数量
private int size;
// 所有元素
private E[] elements;
// 数组默认容量大小
private static final int DEFAULT_CAPACITY = 2;
private static final int ELEMENT_NOT_FOUND = -1;
public ArrayList(int capacity){
capacity = (capacity < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;
elements = (E[]) new Object[capacity];
}
public ArrayList(){
this(DEFAULT_CAPACITY);
}
private void outOfBound(int index){
throw new ArrayIndexOutOfBoundsException("Index:" + index + ",Size:" + size);
}
private void rangeCheck(int index){
if (index < 0 || index >= size){
outOfBound(index);
}
}
private void rangeCheckForAdd(int index){
if (index < 0 || index > size){
outOfBound(index);
}
}
private void ensureCapacity(int capacity){
int oldCapacity = elements.length;
if(oldCapacity >= capacity) return;
// 新容量为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
E[] newElements = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
}
/**
* 元素的数量
* @return
*/
public int size(){
return size;
}
/**
* 是否为空
* @return
*/
public boolean isEmpty(){
return size == 0;
}
/**
* 是否包含某个元素
* @param element
* @return
*/
public boolean contains(E element){
return indexOf(element) != ELEMENT_NOT_FOUND;
}
/**
* 添加元素到最后面
* @param element
*/
public void add(E element){
add(size, element);
}
/**
* 返回index位置对应的元素
* @param index
* @return
*/
public E get(int index){
rangeCheck(index);
return elements[index];
}
/**
* 设置index位置的元素
* @param index
* @param element
* @return
*/
public E set(int index, E element){
rangeCheck(index);
E old = elements[index];
elements[index] = element;
return old;
}
/**
* 往index位置添加元素
* @param index
* @param element
*/
public void add(int index, E element){
rangeCheckForAdd(index);
ensureCapacity(size + 1);
for (int i = size - 1; i >= index; i--) {
elements[i+1] = elements[i];
}
elements[index] = element;
size++;
}
/**
* 删除index位置对应的元素
* @param index
* @return
*/
public E remove(int index){
rangeCheck(index);
E old = elements[index];
for (int i = index + 1; i <= size - 1; i++) {
elements[i -1] = elements[i];
}
elements[--size] = null;
return old;
}
/**
* 查看元素的位置
* @param element
* @return
*/
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(){
for (int i = 0; i < size; i++) {
elements[i] = null;
}
size = 0;
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("size = ").append(size).append(", [");
for (int i = 0; i < size; i++) {
if (i != 0) {
stringBuffer.append(", ");
}
stringBuffer.append(elements[i]);
}
stringBuffer.append("]");
return stringBuffer.toString();
}
}