当前位置:   article > 正文

数据结构之ArrayList与顺序表(上)

数据结构之ArrayList与顺序表(上)

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏:数据结构(Java版)

顺序表的学习,点我

上面这篇博文是关于顺序表的基础知识,以及顺序表的实现。

目录

手动实现顺序表的源码:

分析Java 8 的 ArrayList 的源码 

字段:

构造方法:

ArrayList本身的扩容机制和分配内存机制

ArrayList常见操作

ArrayList的遍历 

普通for循环遍历

for-each遍历 

toString方法遍历 

迭代器遍历 


手动实现顺序表的源码:

下面是Java版的顺序表源码:

  1. public class MyArraylist {
  2. public int[] elem;
  3. public int usedSize;//0
  4. //默认容量
  5. private static final int DEFAULT_SIZE = 10;
  6. public MyArraylist() {
  7. this.elem = new int[DEFAULT_SIZE];
  8. }
  9. /**
  10. * 打印顺序表:
  11. * 根据usedSize判断即可
  12. */
  13. public void display() {
  14. for (int i = 0; i < this.usedSize; i++) {
  15. System.out.print(this.elem[i]+" ");
  16. }
  17. System.out.println();
  18. }
  19. // 新增元素,默认在数组最后新增
  20. public void add(int data) {
  21. // 增加元素之前得先判断数组是否已满
  22. if (isFull()) {
  23. expansion();
  24. }
  25. // 尾插数据不需要判断 pos 位置是否合法
  26. elem[this.usedSize++] = data;
  27. }
  28. // 扩容
  29. private void expansion() {
  30. // 原来数组的2倍扩容
  31. this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
  32. }
  33. /**
  34. * 判断当前的顺序表是不是满的!
  35. * @return true:满 false代表空
  36. */
  37. public boolean isFull() {
  38. // 判断这个数组的有效元素个数是否等于数组的长度
  39. return this.usedSize == this.elem.length;
  40. }
  41. private boolean checkPosInAdd(int pos) {
  42. if (pos < 0 || pos > this.usedSize) {
  43. return false;
  44. }
  45. return true;//合法
  46. }
  47. // 在 pos 位置新增元素
  48. public void add(int pos, int data) throws PosofAddException{
  49. if (!checkPosInAdd(pos)) {
  50. throw new PosofAddException("add(int pos, int data) " +
  51. "pos位置不合法");
  52. }
  53. // 判断是否为尾插
  54. if (pos == this.usedSize) {
  55. add(data);
  56. }else {
  57. // 开始移除元素(从后往前移除)
  58. for (int i = this.usedSize; i > pos; i--) {
  59. this.elem[i] = this.elem[i-1];
  60. }
  61. // 开始插入数据
  62. this.elem[pos] = data;
  63. this.usedSize++;
  64. }
  65. }
  66. // 判定是否包含某个元素
  67. public boolean contains(int toFind) {
  68. // 先判断这个数组是否有元素
  69. if (isEmpty()) {
  70. return false;
  71. }
  72. for (int i = 0; i < this.usedSize; i++) {
  73. if (this.elem[i] == toFind) {
  74. return true;
  75. }
  76. }
  77. return false;
  78. }
  79. // 查找某个元素对应的位置
  80. public int indexOf(int toFind) {
  81. for (int i = 0; i < this.usedSize; i++) {
  82. if (this.elem[i] == toFind) {
  83. return i;
  84. }
  85. }
  86. return -1;
  87. }
  88. // 获取 pos 位置的元素
  89. public int get(int pos) throws PosofGetException{
  90. if (pos < 0 || pos > this.usedSize) {
  91. throw new PosofGetException("get(int pos)" +
  92. "pos位置不合法");
  93. }
  94. return this.elem[pos];
  95. }
  96. private boolean isEmpty() {
  97. // 有效数据个数为0,就是为空
  98. return this.usedSize == 0;
  99. }
  100. // 给 pos 位置的元素设为【更新为】 value
  101. public void set(int pos, int value) throws PosofSetException{
  102. // 先得判断这个 pos 位置是否合法
  103. if (pos < 0 || pos >= this.usedSize) {
  104. throw new PosofSetException("set(int pos, int value)" +
  105. "要修改的位置不合法");
  106. }
  107. this.elem[pos] = value;
  108. }
  109. /**
  110. * 删除第一次出现的关键字key
  111. * @param key
  112. */
  113. public void remove(int key) throws PosofRemoveException{
  114. int pos = indexOf(key); // 下标
  115. if (pos < 0) {
  116. throw new PosofRemoveException("remove(int key)" +
  117. "没有您要删除的数据");
  118. }
  119. for (int i = pos; i < this.usedSize - 1; i++) {
  120. // 后一个位置往前覆盖
  121. this.elem[i] = this.elem[i+1];
  122. }
  123. this.usedSize--;
  124. }
  125. // 获取顺序表长度
  126. public int size() {
  127. return this.usedSize;
  128. }
  129. // 清空顺序表
  130. public void clear() {
  131. // 由于存放的是基本数据类型,直接清空即可
  132. this.usedSize = 0;
  133. // 如果存放的是引用数据类型就得通过for循环遍历把数组的内容置为null
  134. // 注意:如果直接把数组置为null的话,就会存在安全问题,而且源码也是遍历的方式
  135. }
  136. }

异常:

PosofAddException:

  1. public class PosofAddException extends RuntimeException{
  2. public PosofAddException() {
  3. }
  4. public PosofAddException(String msg) {
  5. super(msg);
  6. }
  7. }

 PosofGetException:

  1. public class PosofGetException extends RuntimeException{
  2. public PosofGetException() {
  3. }
  4. public PosofGetException(String msg) {
  5. super(msg);
  6. }
  7. }

PosofSetException: 

  1. public class PosofSetException extends RuntimeException{
  2. public PosofSetException() {
  3. }
  4. public PosofSetException(String msg) {
  5. super(msg);
  6. }
  7. }

PosofRemoveException: 

  1. public class PosofRemoveException extends RuntimeException{
  2. public PosofRemoveException() {
  3. }
  4. public PosofRemoveException(String msg) {
  5. super(msg);
  6. }
  7. }

分析Java 8 的 ArrayList 的源码 

实现了咱们自己写的顺序表了之后,就该来看Java本身的源码是怎么写的以及与我们的有什么不同?(注意:由于Java 17封装性太强,不好观看源码,因此下面的源码来自Java 8。)

字段:

elementData 就是我们自己实现的 elem 数组。

size 就是 usedSize ,也就是这个数组的有效元素的个数。 

构造方法:

Java 8 实现的顺序表有三个构造方法。 

带有一个 int 类型的参数的构造方法: 

 不带参数的构造方法:

带一个 Collection 类型的参数的构造方法:

下面的是其源码: 

首先,我们得知道Collection 到底是这个啥? 

根据上面这个图可以得知:Collection 是一个接口。

上面的参数先不看泛型,那就是Collection c  这个意味着只要实现了Collection 接口,就可以被当成实参传过来。而从上图的关系可知:ArrayList 继承了 AbstractLIst 这个抽象类 ,并且实现了LIst这个接口,而 LIst 这个接口拓展了 Collection 这个接口的功能。也就意味着 ArrayList 这个类实现了 Collection 这个接口。那么 ArrayList 就可以被当成参数传过来。

接下来,就是了解 <? extends E> ,?就可以当成是被传过来的 ArrayList 中的泛型,也就是说被传过来的 ArrayList 中的泛型要是 E 或者 E的子类。例如:

了解了这些,我们就来看源码吧。这个源码的大概意思是把传入的 ArrayList 中的元素给全部拷贝到原来的 ArrayList 的后面。

ArrayList本身的扩容机制和分配内存机制

既然我们在创建一个顺序表的时候,原本是空,那么当我们去增加元素的时候,扩容的机制又是如何呢?

上面是分析过程(可忽略),总之,得出了下面这个结论: 

当顺序表为空时,我们如果去增加元素,就会初始化一个大小10的数组。并且数组在满了之后,会按照1.5倍的扩容方式来扩容。如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容。

ArrayList常见操作

ArrayList常用方法
boolean add(E e)尾插e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection<? extends E> c)将c 中的元素进行尾插
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个o
E get(int index)获取下标index位置元素
E set(int index, E element)将下标 index 位置元素改为 element
void clear()清空
boolean contains(Object o)判断是否在顺序表中
int indexOf(Object o)返回第一个o所在下标
int lastlndexOf(Object o)返回最后一个o的下标
List<E> subList(int fromlndex, int tolndex)截取部分list

大部分方法,我们都很熟悉。因此这里只展示 addAll()方法 和 subList 方法。

addAll () 方法可以实现 ArrayList 的带Collection 类型的参数的构造方法。

从上面打印的结果可以看出来,ArrayList 是重写了toString 方法的。

从这里我们就可以看出来,被分割的数组应该是下面这样:

ArrayList的遍历 

普通for循环遍历

  1. public class Test {
  2. public static void main(String[] args) {
  3. ArrayList<Integer> arrayList = new ArrayList<>();
  4. arrayList.add(1);
  5. arrayList.add(2);
  6. arrayList.add(3);
  7. for (int i = 0; i < arrayList.size(); i++) {
  8. System.out.print(arrayList.get(i)+" ");
  9. }
  10. }
  11. }

for-each遍历 

  1. public class Test {
  2. public static void main(String[] args) {
  3. ArrayList<Integer> arrayList = new ArrayList<>();
  4. arrayList.add(1);
  5. arrayList.add(2);
  6. arrayList.add(3);
  7. for (Integer x : arrayList) {
  8. System.out.print(x+" ");
  9. }
  10. }
  11. }

toString方法遍历 

  1. public class Test {
  2. public static void main(String[] args) {
  3. ArrayList<Integer> arrayList = new ArrayList<>();
  4. arrayList.add(1);
  5. arrayList.add(2);
  6. arrayList.add(3);
  7. System.out.println(arrayList.toString());
  8. }
  9. }

迭代器遍历 

  1. public class Test {
  2. public static void main(String[] args) {
  3. ArrayList<Integer> arrayList = new ArrayList<>();
  4. arrayList.add(1);
  5. arrayList.add(2);
  6. arrayList.add(3);
  7. // 调用iterator方法生成一个迭代器,用Iterator<E>来接收
  8. Iterator<Integer> integerIterator = arrayList.iterator();
  9. // 如果下一个元素有值话,就进入while循环,并且打印下一个值,最后自己往后走
  10. while (integerIterator.hasNext()) {
  11. System.out.print(integerIterator.next()+" ");
  12. }
  13. }
  14. }

只要是实现了 Iterator 接口就可以使用迭代器的方式来遍历。就是下面这张图:

好啦!本期 数据结构之ArrayList与顺序表(上)的学习就到此结束啦!我们下一期再一起学习吧!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/715420
推荐阅读
相关标签
  

闽ICP备14008679号