概述
实现我上一篇博客中提到的
实现
根据索引删除元素
/**
* 根据索引删除元素
* 思路:目标索引处往后的元素都往前移动一个位置 就把目标索引处的值给覆盖了 即删除
*
* 已有数组
* 元素 1 2 3 4 5
*
* 索引 0 1 2 3 4
*
* 现在要删除索引为2的元素 则索引为3、4的元素(要移动2个元素 2 = size - index-1)往前移动一个位置
* 即 System.arraycopy()方法中 srcPos 为 index+1
* 就把索引为 2 的元素覆盖掉了
* 索引为 3、4 的元素移动后 索引就变为了 2、3(开始索引为2 即destPos为2 2即index) 所以System.arraycopy()方法中destPos为index
* @param index 目标索引
* @return 返回被删掉的元素
*/
public Integer removeByIndex(int index) {
if (index < 0 || index > size) {
log.error("索引:{} 不正确", index);
return null; // 返回null表示 没有任何一个元素被删掉
}
int removedEle=arr[index]; // 待删除的元素
System.arraycopy(arr, index + 1, arr, index, size - index - 1);
size--; // 删除一个元素后 size自然就要-1
return removedEle;
}
根据元素值删除元素
/**
* 根据值删除元素
* 思路:遍历数组 直到找到目标值 再调用removeByIndex()删掉即可
*
* @param value 欲删除的元素的值
* @return
*/
public int removeByValue(int value) {
int removedIndex = -1;
for (int i = 0; i < size; i++) {
if (arr[i] == value) {
removedIndex = i;
break;
}
}
if (removedIndex == -1) { // 说明待删除元素在数组中不存在 提示
log.info("待删除元素:{}在数组中不存在", value);
return -1;
}
// 调用根据索引删除元素的方法即可
return removeByIndex(removedIndex);
}
测试用例
package com.lovehena.datastructure.test;
import com.lovehena.datastructure.MyArrayList;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TestMyArrayList {
public static void main(String[] args) {
MyArrayList list = new MyArrayList();
list.add(0, 1);
list.add(1, 2);
list.add(2, 3);
list.add(3, 4);
list.add(4, 5);
list.add(5, 6);
list.add(6, 7);
list.add(7, 8);
list.add(8, 9);
list.add(9, 10);
list.addLast(11);
list.print();
list.add(0, 100); // 把100放在第一个位置
/*Consumer<Integer> c1 = new Consumer<Integer>() { // 匿名内部类的写法创建Consumer的实例
@Override
public void accept(Integer element) {
log.info("数组中的元素为:{}", element);
}
};
list.print(c1);*/
list.print();
list.add(8,88);
list.add(9,99);
list.add(10,1000);
list.add(11,111);
list.add(12,122);
list.print();
// 测试删除 removeByIndex
Integer removed = list.removeByIndex(0);
log.info("删除第一个元素:{} 后",removed);
list.print();
Integer removed2 = list.removeByIndex(15);
log.info("再删掉最后一个元素:{} 后",removed2);
list.print();
Integer removed3 = list.removeByIndex(4);
log.info("删掉第五个元素:{} 后",removed3);
list.print();
// 测试删除 removeByValue
int removed4 = list.removeByValue(10);
log.info("删除元素:{} 后",10);
list.print();
}
}
测试用例输出
完整实现
package com.lovehena.datastructure;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Consumer;
/*
* java实现一个动态数组
* 实际上,java.util.ArrayList这个类就是一个动态数组的类
* 和静态数组最大的不同就是,java.util.ArrayList中的元素个数是可以变化的。
*
* 一个动态数组中至少要有3个属性:
* 1.size 动态数组中已有元素的个数
* 2.capacity 动态数组中最大的元素个数
* 3.arr 虽然我们要实现的是一个动态数组,但还是需要一个静态数组用于存储元素,当静态数组无法容纳更多元素时动态扩容,于是,动态数组就实现了
*
* 先实现以下3个方法:
* 1.add(int index,int element) 往数组中指定索引处添加元素
* 2.addLast(int element) 将欲添加的元素添加到数组最后面
* 3.print() 数组的遍历
* */
@Slf4j
public class MyArrayList {
private int size = 0; // 动态数组中已手动添加的元素的个数 初始时一个手动添加的元素也没有
private int capacity = 10; // 动态数组中最大的元素个数 默认为10 java.util.ArrayList的默认初始容量也是10
// 初始化一个静态数组 用于存储元素
// 数组中的元素类型默认都是整数 如果需要存储其他类型 可以利用泛型机制实现
// 实际上 int[] arr=new int[capacity] 这行代码执行完时 arr中已经有10个元素了 并且每个元素的值都是0
private int[] arr = new int[capacity];
/**
* 往数组中指定索引处添加元素
* 思路:
* 已有数组
* 元素 1 2 3 4 5
* 索引 0 1 2 3 4 5
*
* 现在要往索引为2的位置添加一个元素100,则 3 4 5这3( 3 = size - index )个元素都要往后移动1位,将索引为2的位置空出来,再将目标元素插入在索引2处即可
* 保证占用的内存空间是连续的
* @param index 目标索引
* @param element 目标元素
*/
public void add(int index, int element) {
if (size == capacity) { // 原容量已满还要添加新元素 则需要扩容
grow();
}
if (index < 0 || index > size) { // index最大值为size-1 最小值为0
log.error("索引:{} 不正确", index);
return;
}
if (index == size) { // 往数组最后一个位置添加一个元素 不需要移动任何元素
arr[index] = element;
size++;
return;
}
// 利用System.arraycopy完成数组中元素的移动 api不熟悉的话可以点进源码阅读下注释
// destPos的最大值为index(而不是index+1 java api设计中大多都是含头不含尾)
System.arraycopy(arr, index, arr, index + 1, size - index);
arr[index] = element;
size++; // 已手动添加的元素的个数+1 这个size其实类似于实现链表时的头节点(头指针)
}
/**
* 扩容:
* 创建一个新数组,数组长度为原数组的1.5倍(java.util.ArrayList中每次扩容就是1.5倍),将原数组中的元素拷贝到新数组中即可
*/
private void grow() {
int oldCapacity = capacity; // 记录旧容量
int newCapacity = capacity + capacity / 2;// 1.5倍的capacity
int[] newArr = new int[newCapacity];
System.arraycopy(arr, 0, newArr, 0, size); // 把原数组中手动添加的元素全都拷贝到新数组中
arr = newArr;
capacity = newCapacity;
log.info("发生了扩容 扩容前静态数组长度为:{},扩容后的长度为:{}", oldCapacity, newCapacity);
}
/**
* 将欲添加的元素添加到数组最后面
* 思路:
* 已有数组
* 元素 1 2 3 4 5
* 索引 0 1 2 3 4
*
* 现在要将6插入到数组的最后面,即插入到索引为5( size = 5)的位置,所以调用add传入index为size即可
*
* 实际上 很多代码的书写都是先总结规律 再写出来的
* @param element 欲添加的元素
*/
public void addLast(int element){
// 直接调用add方法即可
add(size,element);
}
/**
* 根据索引删除元素
* 思路:目标索引处往后的元素都往前移动一个位置 就把目标索引处的值给覆盖了 即删除
*
* 已有数组
* 元素 1 2 3 4 5
*
* 索引 0 1 2 3 4
*
* 现在要删除索引为2的元素 则索引为3、4的元素(要移动2个元素 2 = size - index-1)往前移动一个位置
* 即 System.arraycopy()方法中 srcPos 为 index+1
* 就把索引为 2 的元素覆盖掉了
* 索引为 3、4 的元素移动后 索引就变为了 2、3(开始索引为2 即destPos为2 2即index) 所以System.arraycopy()方法中destPos为index
* @param index 目标索引
* @return 返回被删掉的元素
*/
public Integer removeByIndex(int index) {
if (index < 0 || index > size) {
log.error("索引:{} 不正确", index);
return null; // 返回null表示 没有任何一个元素被删掉
}
int removedEle=arr[index]; // 待删除的元素
System.arraycopy(arr, index + 1, arr, index, size - index - 1);
size--; // 删除一个元素后 size自然就要-1
return removedEle;
}
/**
* 根据值删除元素
* 思路:遍历数组 直到找到目标值 再调用removeByIndex()删掉即可
*
* @param value 欲删除的元素的值
* @return
*/
public int removeByValue(int value) {
int removedIndex = -1;
for (int i = 0; i < size; i++) {
if (arr[i] == value) {
removedIndex = i;
break;
}
}
if (removedIndex == -1) { // 说明待删除元素在数组中不存在 提示
log.info("待删除元素:{}在数组中不存在", value);
return -1;
}
// 调用根据索引删除元素的方法即可
return removeByIndex(removedIndex);
}
/**
* Consumer的泛型表示调用者拿到的操作数的类型 本例中调用者需要拿到每个数组的元素(int类型)
* 类似于js中的回调函数
* @param cb 调用者手动指定遍历数组的逻辑
*/
public void print(Consumer<Integer> cb){ //
/*
为什么不是这么写?
for (int i = 0; i < arr.length; i++) {
}
因为 数组中真正添加了几个元素是由size表示的 我们遍历时需要的是我们手动添加的元素
而不是数组初始化时默认初始化的为0的元素
*/
for (int i = 0; i < size; i++) {
cb.accept(arr[i]);
}
}
/**
* 数组遍历
*/
public void print(){
/*
为什么不是这么写?
for (int i = 0; i < arr.length; i++) {
}
因为 数组中真正添加了几个元素是由size表示的 我们遍历时需要的是我们手动添加的元素
而不是数组初始化时默认初始化的为0的元素
*/
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arr[i]).append(" ");
}
log.info("遍历数组 数组为:{}\n",sb.toString());
}
// todo 扩展:根据元素值删除元素时,如果数组中存在多个目标元素该怎么处理?
}
扩展
根据元素值删除元素时,如果数组中存在多个目标元素该怎么处理?
最后
好了,如果对你有帮助的话,欢迎点个免费的赞哦。