目录
1.线性表
线性表( linear list) 是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
2.1 接口的实现
public interface SeqList<E> {
// 尾插
void add(E element);
// 将 element 插入到 index 位置
void add(int index,E element);
// 删除 index 位置元素<返回删除的值
E removeByIndex(int index);
// 删除第一个值element的元素
void removeByValue(E element);
// 删除所有值element的元素
void removeAllValue(E element);
// 将下标 index 位置元素设置为 element,返回替换前的值
E set(int index,E element);
E get(int index);
// 判断 o 是否在其中
boolean contains(E element);
int indexOf(E element);
int size();
void clear();
}
3.自己动手实现ArrayList
3.1 重写SeqList接口方法
public class MyArray<E> implements SeqList<E> {
private Object[] elementData;
// 保存目前线性表中真正保存的有效元素个数
private int size;
// 默认开辟数组长度10
public MyArray(){
this(10);
}
// 从外部传入一个数组长度进行初始化
public MyArray(int size){
this.elementData = new Object[size];
}
@Override
public void add(E element) {}
@Override
public void add(int index, E element) {}
@Override
public E removeByIndex(int index) {}
@Override
public void removeByValue(E element) {}
@Override
public void removeAllValue(E element) {}
@Override
public E set(int index, E element) {}
@Override
public boolean contains(E element) {}
@Override
public int indexOf(E element) {}
@Override
public String toString() {}
}
3.2 在数组最后新增元素
(1)将下标为size的elementData数组进行赋值element
(2)size长度加1
(3)判断数组是否满了,实际长度是否等于数组长度,如果是,进行扩容
为什么不先判断数组是否满了再进行添加?
我们使用的是add()方法对数组进行添加操作的,每添加一次都会判断数组是否满了,为下一个元素添加做准备,因此每次添加时,数组一定是没满的,size下标是可以存放元素的
public void add(E element) {
// size恰好是下一个保存属性的下标
this.elementData[size] = element;
size ++;
// 实际长度 == 数组长度,对数组进行扩容
if(size == elementData.length){
grow();
}
}
3.3 对内部数组进行扩容
判断数组满了的时候,对数组扩容
对原来长度左移一位,新长度为原来的两倍
再使用Arrays.copyOf对原数组进行拷贝
private void grow(){
int oldLength = elementData.length;
int newLength = oldLength << 1;
Object[] newArray = Arrays.copyOf(elementData,newLength);
elementData = newArray;
}
3.4 在 index位置新增元素
(1)判断index是否合法,不合法抛出IllegalArgumentException异常(index < 0 || index > size)(2)如果index刚好是size,那就是再数组的最后一位添加,直接调用add()方法就好了(3)将下标为index以及之后的元素往后移一位,再将index下标的元素改为element比如:原数组为 0 1 2 3 4 5,在index为2的下标插入元素6,首先将下标为index以及之后的元素往后移一位:0 1 2 2 3 4 5,再将index下标的元素改为element:0 1 6 2 3 4 5(4)判断数组是否满了,实际长度是否等于数组长度,如果是,进行扩容
public void add(int index, E element) {
// 判断index是否合法??
if(!rangeCheck(index)){
throw new IllegalArgumentException("add index Illegal");
}
// 尾插
if(index == size){
add(element);
return;
}
// 0 <= index <size
for (int i = size -1; i >= index ; i--) {
elementData[i +1] = elementData[i];
}
// 装入插入的元素
elementData[index] = element;
size ++;
if(size == elementData.length){
grow();
}
}
3.5 对下标的合法性校验
private boolean rangeCheck(int index){
if (index < 0 || index >= size) {
return false;
}
return true;
}
3.6 删除第一次出现的下标为index的元素
(1)判断index是否合法,不合法抛出IllegalArgumentException异常
(2)从待删除元素开始,将当前位置的值覆盖为下一个元素的值
比如原数组为 0 1 2 3 4 5,待删除元素下标为2,对元素进行覆盖:0,1,3,4,5,5
(3)size减1,有效数组元素为0,1,3,4,5,返回修改前的值
(4)如果是引用类型,需要将最后一个位置的元素置为空,否则会出现内存泄漏!!!
public E removeByIndex(int index) {
// 1.index合法性校验
if(!rangeCheck(index)) {
throw new IllegalArgumentException("remove index illegal!");
}
E oldVal = (E) elementData[index];
// 从待删除元素开始,将当前位置的值覆盖为下一个元素的值
// 由于删除场景下索引不能取到size
// 因此要保证代码中所有位置均不越界,i + 1 < size
for (int i = index; i < size - 1; i++) {
elementData[i] = elementData[i + 1];
}
// 维护一下size属性,最后多的那个元素不影响我们的使用~下次添加元素时就会自动覆盖掉~
size --;
return oldVal;
}
3.7 删除第一个值element的元素
从前往后找到这个元素的下标,使用removeByIndex(index)方法进行删除,删除后退出方法
public void removeByValue(E element) {
// 从前向后遍历,碰到第一个值为待删除的元素时,删除该元素并返回 ~
for (int i = 0; i < size; i++) {
if (elementData[i].equals(element)) {
// 碰到第一个待删除的元素
removeByIndex(i);
return;
}
}
System.out.println("没有待删除的元素!");
}
3.8 删除所有值element的元素
从前往后找到这个元素的下标,使用removeByIndex(index)方法进行删除
为什么不能删除一次就i++?
举个例子说明:原数组为 0 1 2 2 3 4 5,待删除的元素为2,当我遍历到第一个待删除的元素2时,i为2,此时调用 removeByIndex(i)进行删除,删除后数组元素为 0 1 2 3 4 5 ,如果进行i++,那么此时i为3,就会少删除现在数组中的2(下标为2)。
public void removeAllValue(E element) {
// 从前向后遍历,碰到索引对应的元素为待删除的元素,删除即可
for (int i = 0; i < size;) {
if (elementData[i].equals(element)){
// 当i指向的元素是待删除的元素,不能移动i
removeByIndex(i);
}else {
// 此时i指向的不是待删除元素,此时移动i的指向,继续判断下一个元素是否是待删除的元素
i ++;
}
}
}
3.9 修改下标为index的元素为element
public E set(int index, E element) {
if(!rangeCheck(index)) {
throw new IllegalArgumentException("set index illegal!");
}
E oldVal = (E) elementData[index];
elementData[index] = element;
return oldVal;
}
3.10 获取下标为index的元素
public E get(int index) {
if (!rangeCheck(index)) {
throw new IllegalArgumentException("get index illegal!");
}
return (E) elementData[index];
}
3.11 获取元素值为element的下标
public int indexOf(E element) {
for (int i = 0; i < size; i++) {
if (elementData[i].equals(element)) {
return i;
}
}
throw new NoSuchElementException("indexOf error!" +
"Don't has this element");
}
3.12 判断数组中是否存在element
public boolean contains(E element) {
// foreach会遍历所有的数组元素,包括无效元素!
// 无效元素此时为null,出现空指针异常
// for (Object e : elementData) {
// if (e.equals(element)) {
// return true;
// }
// }
// return false;
for (int i = 0; i < size; i++) {
if (elementData[i].equals(element)) {
return true;
}
}
return false;
}
3.13 打印顺序表
public String toString() {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < size; i++) {
sb.append(elementData[i]);
if(i != size - 1){
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
3.14 获取顺序表长度以及清空顺序表
// 获取顺序表长度
public int size() {
return this.usedSize;
}
// 清空顺序表
public void clear() {
/*
如果是引用数据类型 得一个一个置为空 这样做才是最合适的
for (int i = 0; i < this.usedSize; i++) {
this.elem[i] = null;
}
this.usedSize = 0;
*/
for (int i = 0; i < usedSize; i++) {
elem[i] = 0;
}
this.usedSize = 0;
}
4.MyArray整体实现
4.1 MyArray类
import java.util.Arrays;
import java.util.NoSuchElementException;
public class MyArray<E> implements SeqList<E> {
private Object[] elementData;
// 保存目前线性表中真正保存的有效元素个数
private int size;
// 默认开辟数组长度10
public MyArray(){
this(10);
}
// 从外部传入一个数组长度进行初始化
public MyArray(int size){
this.elementData = new Object[size];
}
@Override
public void add(E element) {
// size恰好是下一个保存属性的下标
this.elementData[size] = element;
size ++;
// 实际长度 = 数组长度
if(size == elementData.length){
grow();
}
}
// 对内部数组进行扩容
private void grow(){
int oldLength = elementData.length;
int newLength = oldLength << 1;
Object[] newArray = Arrays.copyOf(elementData,newLength);
elementData = newArray;
}
@Override
public void add(int index, E element) {
// 判断index是否合法??
if(index < 0 || index > size){
throw new IllegalArgumentException("add index Illegal");
}
// 尾插
if(index == size){
add(element);
return;
}
// 0 <= index <size
for (int i = size -1; i >= index ; i--) {
elementData[i +1] = elementData[i];
}
// 装入插入的元素
elementData[index] = element;
size ++;
if(size == elementData.length){
grow();
}
}
@Override
public E removeByIndex(int index) {
// 1.index合法性校验
if(!rangeCheck(index)) {
throw new IllegalArgumentException("remove index illegal!");
}
E oldVal = (E) elementData[index];
// 从待删除元素开始,将当前位置的值覆盖为下一个元素的值
// 由于删除场景下索引不能取到size
// 因此要保证代码中所有位置均不越界,i + 1 < size
for (int i = index; i < size - 1; i++) {
elementData[i] = elementData[i + 1];
}
// 维护一下size属性,最后多的那个元素不影响我们的使用~下次添加元素时就会自动覆盖掉~
size --;
return oldVal;
}
@Override
public void removeByValue(E element) {
// 从前向后遍历,碰到第一个值为待删除的元素时,删除该元素并返回 ~
for (int i = 0; i < size; i++) {
if (elementData[i].equals(element)) {
// 碰到第一个待删除的元素
removeByIndex(i);
return;
}
}
System.out.println("没有待删除的元素!");
}
@Override
public void removeAllValue(E element) {
// 从前向后遍历,碰到索引对应的元素为待删除的元素,删除即可
for (int i = 0; i < size;) {
if (elementData[i].equals(element)){
// 当i指向的元素是待删除的元素,不能移动i
removeByIndex(i);
}else {
// 此时i指向的不是待删除元素,此时移动i的指向,继续判断下一个元素是否是待删除的元素
i ++;
}
}
}
@Override
public E set(int index, E element) {
if(!rangeCheck(index)) {
throw new IllegalArgumentException("set index illegal!");
}
E oldVal = (E) elementData[index];
elementData[index] = element;
return oldVal;
}
// private boolean ran
@Override
public E get(int index) {
if (!rangeCheck(index)) {
throw new IllegalArgumentException("get index illegal!");
}
return (E) elementData[index];
}
private boolean rangeCheck(int index){
if (index < 0 || index >= size) {
return false;
}
return true;
}
@Override
public boolean contains(E element) {
// foreach会遍历所有的数组元素,包括无效元素!
// 无效元素此时为null,出现空指针异常
// for (Object e : elementData) {
// if (e.equals(element)) {
// return true;
// }
// }
// return false;
for (int i = 0; i < size; i++) {
if (elementData[i].equals(element)) {
return true;
}
}
return false;
}
@Override
public int indexOf(E element) {
for (int i = 0; i < size; i++) {
if (elementData[i].equals(element)) {
return i;
}
}
throw new NoSuchElementException("indexOf error!" +
"Don't has this element");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < size; i++) {
sb.append(elementData[i]);
if(i != size - 1){
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
// 获取顺序表长度
public int size() {
return this.size;
}
// 清空顺序表
public void clear() {
/*
如果是引用数据类型 得一个一个置为空 这样做才是最合适的
for (int i = 0; i < this.usedSize; i++) {
this.elem[i] = null;
}
this.usedSize = 0;
*/
for (int i = 0; i < size; i++) {
elementData[i] = 0;
}
this.size = 0;
}
}
4.2 Test类
package seqlist.array;
import seqlist.SeqList;
public class MyArrayTest {
public static void main(String[] args) {
SeqList<Integer> list = new MyArray<Integer>(4);
list.add(1);
list.add(2);
list.add(3);
list.add(9);
list.add(10);
list.add(10);
list.add(6);
list.add(10);
list.add(5);
list.add(7);
list.add(10);
list.add(10);
System.out.println(list);
System.out.println("------------添加测试-----------");
System.out.println("从数组尾部添加99");
list.add(99);
System.out.println("添加index为2,element为88");
list.add(2,88);
System.out.println(list);
System.out.println("-----------删除测试------------");
System.out.println("删除index为0");
list.removeByIndex(0);
System.out.println("删除元素为6");
list.removeByValue(6);
System.out.println("删除所有10");
list.removeAllValue(10);
System.out.println(list);
System.out.println("-----------其他------------");
System.out.println("查看是否包含10这个元素");
System.out.println(list.contains(10));
System.out.println("修改index为3,element为19");
list.set(3,19);
System.out.println("获取index为3的元素");
System.out.println(list.get(3));
System.out.println(list);
System.out.println("获取element为88的index");
System.out.println(list.indexOf(88));
System.out.println("获取顺序表长度");
System.out.println(list.size());
System.out.println("清空顺序表");
list.clear();
System.out.println(list);
}
}
4.3 测试结果