线性表之动态数组(ArrayList)的自实现

简介: 线性表之动态数组(ArrayList)的自实现

一、数组是什么?


数组是一种顺序存储的线性表,所有元素的内存地址是连续的

int[] array = new int[]{11,22,33}


1667908165765.jpg

数组的缺点就是无法修改其容量,但是在实际开发过程中往往需要动态修改容量的。


二、对象数组


Object[] objects = new Object[7];

1667908195102.jpg

在这里我引入了对象数组,是因为这里会设计到一些内存管理上的细节问题。


在清除所有元素的时候,代码如下:

public void clear() {
  for (int i = 0; i < size; i++) {
    elements[i] = null;
  }
  size = 0;
  }

在这里我们要知道,如果Objects指向的地址丢失了,那么数组和对象谁先消失呢?


因为对象是依赖于地址的,所以一定是对象最后才被清除消失。因为JVM的垃圾回收机制一般不会马上进行清除,那么此时就会造成大量空间的浪费,而且再次放元素还要进行频繁的空间创建,这样就会开销很大。所以我们在进行元素清除的时候只需要进行把地址的位置置为null即可。


同样,我们在删除元素的时候也是这样置为null,如下:


public E remove(int index) {
  rangeCheck(index);
  E old = elements[index];
  for (int i = index + 1; i < size; i++) {
    elements[i - 1] = elements[i];
  }
  elements[--size] = null;
  return old;
  }


三、动态数组的设计


1.泛型


使用泛型技术可以让动态数组更加通用,可以存放任何数据类型。


另外一个重要的点是任何类的父类,我们都可以认为是Object的子类。


还提供了有参和无参两个构造方法!!!

public class ArrayList {
  private int size;
  private E[] elements;
  private static final int DEFAULT_CAPACITY = 10;  //设置初始数组的容量为10
  private static final int ELEMENT_NOT_FOUND = -1; //把查找不到的时候返回值统一设为默认值
  public ArrayList(int capaticy) {
  capaticy = (capaticy < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capaticy;
  elements = (E[]) new Object[capaticy];
  }
  public ArrayList() {
  this(DEFAULT_CAPACITY);
  }
}


2.清除所有元素


public void clear() {
  for (int i = 0; i < size; i++) {
    elements[i] = null;
  }
  size = 0;
  }


3.元素的数量


public int size() {
  return size;
  }


4.判断是否为空


public boolean isEmpty() {
   return size == 0;
  }


5.是否包含某个元素


public boolean contains(E element) {
   return indexOf(element) != ELEMENT_NOT_FOUND;
}


6.添加元素到尾部


public void add(E element) {
  add(size, element);
  }


7.获取index位置的元素


public E get(int index) {
  rangeCheck(index);
  return elements[index];
  }


8.设置index位置的元素


public E set(int index, E element) {
  rangeCheck(index);
  E old = elements[index];
  elements[index] = element;
  return old;
  }


9.在index位置插入一个元素


public void add(int index, E element) {
  rangeCheckForAdd(index);
  ensureCapacity(size + 1);
  for (int i = size; i > index; i--) {
    elements[i] = elements[i - 1];
  }
  elements[index] = element;
  size++;
  }


10.删除index位置的元素


public E remove(int index) {
  rangeCheck(index);
  E old = elements[index];
  for (int i = index + 1; i < size; i++) {
    elements[i - 1] = elements[i];
  }
  elements[--size] = null;
  return old;
  }


11.查看元素的索引


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; // n
    }
  }
  return ELEMENT_NOT_FOUND;
  }


12.保证要有capacity的容量


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;
  }


13.溢出抛异常


private void outOfBounds(int index) {
  throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
  }


14.一般索引检查


private void rangeCheck(int index) {
  if (index < 0 || index >= size) {
    outOfBounds(index);
  }
  }


15.添加元素时索引检查(在尾部可以添加,所以可以等于Size)


private void rangeCheckForAdd(int index) {
  if (index < 0 || index > size) {
    outOfBounds(index);
  }
  }


16.打印


public String toString() {
  StringBuilder string = new StringBuilder();
  string.append("size=").append(size).append(", [");
  for (int i = 0; i < size; i++) {
    if (i != 0) {
    string.append(", ");
    } 
    string.append(elements[i]);
         }
        string.append("]");
  return string.toString();


四、动态数组复杂度分析


数组的随机访问是非常快的,因为可以直接根据下标索引计算出地址的偏移量,访问很快,而且访问效率与数组元素的个数无关。


动态数组  


最好

最坏

平均

add(intindex,Eelement)

O(1)

O(n)

O(n)









最好

最坏

平均

add(intindex,Eelement)

O(1)

O(n)

O(n)

remove(intindex)

O(1)

O(n)

O(n)

set(intindex,Eelement)

O(1) O(1) O(1)

get(intindex)

O(1) O(1) O(1)


相关文章
|
Linux 网络安全 Android开发
振南技术干货集:各大平台串口调试软件大赏(2)
振南技术干货集:各大平台串口调试软件大赏(2)
|
Kubernetes 搜索推荐 前端开发
containerd 镜像构建工具 -- nerdctl 和 buildkit
containerd 镜像构建工具 -- nerdctl 和 buildkit
7676 0
notepad++选中多行文本
notepad++选中多行文本
961 0
notepad++选中多行文本
|
8月前
|
SQL 关系型数据库 MySQL
如何实现 MySQL 的读写分离?
本文介绍了 MySQL 读写分离的实现方式及其主从复制原理,解释了如何通过主从架构提升读并发能力。重点分析了主从同步延时问题及解决方案,如半同步复制、并行复制等技术手段,并结合实际案例探讨了高并发场景下的优化策略。文章还提醒开发者在编写代码时需谨慎处理插入后立即查询的情况,避免因主从延时导致的数据不一致问题。
1003 44
如何实现 MySQL 的读写分离?
|
Java 数据库连接 数据库
Hibernate 中出现表名(XXX) is not mapped 问题
Hibernate 中出现表名(XXX) is not mapped 问题,检查以下3个原因
1039 0
Hibernate 中出现表名(XXX) is not mapped 问题
|
机器学习/深度学习 算法 PyTorch
Pytorch-RMSprop算法解析
关注B站【肆十二】,观看更多实战教学视频。本期介绍深度学习中的RMSprop优化算法,通过调整每个参数的学习率来优化模型训练。示例代码使用PyTorch实现,详细解析了RMSprop的参数及其作用。适合初学者了解和实践。
303 1
|
SQL 存储 数据库
SQL语句给予用户权限:技巧、方法与最佳实践
在数据库管理中,为用户分配适当的权限是确保数据安全性和操作效率的关键步骤
|
网络安全
如何用HCL模拟器配置防火墙IRF?
如何用HCL模拟器配置防火墙IRF?
356 2
|
自然语言处理 算法 API
「AIGC」Python实现tokens算法
使用Python的`transformers`库,通过`AutoTokenizer`初始化BERT tokenizer,对文本进行分词统计,减少API调用。示例展示从开始到结束的时间,包括文本转换为tokens的数量和过程耗时。
403 0
「AIGC」Python实现tokens算法
|
Java API Android开发
Android中Binder在项目中的具体使用详解
Android中Binder在项目中的具体使用详解
349 0