当前位置:   article > 正文

数据结构 | ArrayList与顺序表

数据结构 | ArrayList与顺序表

集合框架

Java集合框架,又被称为容器container,是定义在 java.util 包下的一组 接口interfaces 和其实现的 类classes .

其主要表现为将多个元素置于一个单元中,用于对这些元素进行快速便捷的 存储store , 检索retrieve , 管理manipulate ,即平时俗称的 增删查改CRUD .

也就是说,集合框架是很多类组成的,每个类的背后就是一种数据结构.

线性表

线性表(linear list) 是n个具有相同特性的数据元素的有限序列.线性表是一种在实际中广泛使用的数据结构,常见的线性表: 顺序表,链表,栈,队列...

线性表在逻辑上是线性结构,是连续的一条址线; 但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储.

顺序表

顺序表是用一段物理地址连续的存储单元 依次存储 数据元素的线性结构,一般情况下采用 数组 存储.在数组上完成数据的增删查改.

顺序表接口的实现

1.在src包底下创建以下类和接口

2.IList接口代码实现

  1. public interface IList {
  2. public void add(int data);
  3. // 在 pos 位置新增元素
  4. public void add(int pos, int data);
  5. // 判定是否包含某个元素
  6. public boolean contains(int toFind);
  7. // 查找某个元素对应的位置
  8. public int indexOf(int toFind);
  9. // 获取 pos 位置的元素
  10. public int get(int pos);
  11. // 给 pos 位置的元素设为 value
  12. public void set(int pos, int value);
  13. //删除第一次出现的关键字key
  14. public void remove(int toRemove);
  15. // 获取顺序表长度
  16. public int size();
  17. // 清空顺序表
  18. public void clear();
  19. // 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
  20. public void display();
  21. boolean isFull();
  22. boolean isEmpty();
  23. }

3.MyArrayList类代码实现

  1. import java.util.Arrays;
  2. public class MyArrayList implements IList {
  3. public int[] elem;
  4. public int usedSize;
  5. //默认的容量
  6. public static final int DEFAULT_CAPACITY = 5;
  7. public MyArrayList() {
  8. elem = new int[DEFAULT_CAPACITY];
  9. }
  10. //添加元素,默认添加到数组的最后位置
  11. @Override
  12. public void add(int data) {
  13. //1.判断数组是否满了,满了要扩容
  14. if(isFull()) {
  15. elem = Arrays.copyOf(elem,2*elem.length);
  16. }
  17. elem[usedSize] = data;
  18. usedSize++;
  19. }
  20. /**
  21. * 给pos位置 添加一个元素(即指定位置插入元素)
  22. * @param pos
  23. * @param data
  24. */
  25. @Override
  26. public void add(int pos, int data) {
  27. //1.pos位置的判断
  28. checkPosOfAdd(pos);
  29. if(isFull()) {
  30. elem = Arrays.copyOf(elem,2*elem.length);
  31. }
  32. /**
  33. * 1.elem的下标i是 最后 一个存储的数据元素的位置,所以是usedSize-1 (移动元素,从后往前移动)
  34. * 2.i下标的元素赋值给i+1下标的元素
  35. * 3.i-- 往前挪一个
  36. * 4.当 i < pos 时结束
  37. * 5.将要添加的元素data 放到elem的pos位置 (元素放进去)
  38. * 6.usedSize++
  39. */
  40. for (int i = usedSize - 1; i >= pos; i--) {
  41. elem[i+1] = elem[i];
  42. }//(移动元素,从后往前移动)
  43. elem[pos] = data;//(元素放进去)
  44. usedSize++;
  45. }
  46. public void checkPosOfAdd(int pos) {
  47. if(pos <0 || pos > usedSize) {
  48. throw new PosException("pos位置为:" + pos);
  49. }
  50. }
  51. //查找当前元素 是否存在
  52. @Override
  53. public boolean contains(int toFind) {
  54. for (int i = 0; i < usedSize; i++) {
  55. if(elem[i] == toFind) {
  56. return true;
  57. }
  58. }
  59. return false;
  60. }
  61. //查找当前元素的下标
  62. @Override
  63. public int indexOf(int toFind) {
  64. for (int i = 0; i < usedSize; i++) {
  65. if(elem[i] == toFind) {
  66. return i;
  67. }
  68. }
  69. return -1;
  70. }
  71. //获取pos位置的值
  72. @Override
  73. public int get(int pos) {
  74. //1.pos位置不合法怎么办
  75. checkPosOfGet(pos);
  76. //2.为空怎么办
  77. if(isEmpty()) {
  78. throw new EmptyException("顺序表为空");
  79. }
  80. return elem[pos];
  81. }
  82. public void checkPosOfGet(int pos) {
  83. if(pos < 0 || pos >= this.usedSize) {
  84. throw new PosException("pos位置不合法" + pos);
  85. }
  86. }
  87. @Override
  88. public boolean isEmpty() {
  89. return usedSize == 0;
  90. }
  91. /**
  92. * 更新pos位置的值为value
  93. * @param pos
  94. * @param value
  95. */
  96. @Override
  97. public void set(int pos, int value) {
  98. checkPosOfGet(pos);
  99. if (isEmpty()) {
  100. throw new EmptyException("顺序表为空");
  101. }
  102. this.elem[pos] =value;
  103. }
  104. /**
  105. * 删除toRemove这个数字
  106. * @param toRemove
  107. */
  108. @Override
  109. public void remove(int toRemove) {
  110. if(isEmpty()) {
  111. throw new PosException("顺序表为空,不能删除");
  112. }
  113. int index = indexOf(toRemove);
  114. for (int i = index; i <usedSize-1 ; i++) {
  115. elem[i] = elem[i+1];
  116. }
  117. usedSize--;
  118. }
  119. @Override
  120. public int size() {
  121. return this.usedSize;
  122. }
  123. /**
  124. *清空顺序表 防止内存泄漏
  125. *
  126. *引用数据类型,将每个对象都置为null
  127. *基本数据类型,将元素置为0
  128. */
  129. @Override
  130. public void clear() {
  131. usedSize = 0;
  132. }
  133. //打印顺序表当中的所有元素
  134. @Override
  135. public void display() {
  136. for (int i = 0; i < usedSize; i++) {
  137. System.out.print(elem[i] +" ");
  138. }
  139. System.out.println();
  140. }
  141. @Override
  142. public boolean isFull() {
  143. return usedSize == elem.length;
  144. }
  145. }

 4.异常类处理

  1. //Pos位置不合法异常
  2. public class PosException extends RuntimeException{
  3. public PosException() {
  4. }
  5. public PosException(String message) {
  6. super(message);
  7. }
  8. }
  9. //顺序表为空异常
  10. public class EmptyException extends RuntimeException{
  11. public EmptyException() {
  12. }
  13. public EmptyException(String message) {
  14. super(message);
  15. }
  16. }

5.Test测试代码实现

  1. public class Test {
  2. public static void main(String[] args) {
  3. MyArrayList myArrayList = new MyArrayList();
  4. myArrayList.add(10);
  5. myArrayList.add(20);
  6. myArrayList.add(30);
  7. myArrayList.add(40);
  8. myArrayList.display();
  9. myArrayList.remove(40);
  10. myArrayList.display();
  11. }
  12. }

 6.运行结果

                     

ArrayList

ArrayList是Java已经实现好的顺序表,我们只需要学习掌握ArrayList里面的方法!

ArrayList的构造方法

当我们学习一个新的类时,一定要先从构造方法入手~

方法解释
ArrayListI()无参构造
ArrayList(int initiCapacity)指定顺序表初始容量
ArrayList( Collection<? extends E> c)利用其他Collection构建ArrayList
(1)无参构造
  1. //构造一个空的列表
  2. ArrayList<Integer> arrayList = new ArrayList<Integer>();
  3. arrayList.add(99);//99存入顺序表 下同
  4. arrayList.add(88);
  5. arrayList.add(75);
  6. arrayList.add(64);
  7. System.out.println(arrayList);

ArrayList是以泛型方式实现的,使用时先实例化.

ArrayList()无参构造就是在创建的时候不需要 参数 去创建,至于他的初始化的内存是多少,是怎么样初始化的,下文会提到.

(2)指定顺序表初始容量
  1. //构造一个容量大小为10的列表
  2. ArrayList<Integer> arrayList = new ArrayList<Integer>(10);
  3. arrayList.add(11);//11存入顺序表 下同
  4. arrayList.add(22);
  5. arrayList.add(75);
  6. arrayList.add(64);
  7. System.out.println(arrayList);

上面的是没有参数的,这里就是有参数的了,在括号里面增加的 参数 就是关于这个顺序表的 初始容量,也就是大小. 

(3)利用其他Collection构建ArrayList
  1. //创建的 参数 其实就是用其他List的大小作为参数
  2. ArrayList<Integer> arrayList = new ArrayList<Integer>();
  3. arrayList.add(99);
  4. arrayList.add(88);
  5. arrayList.add(75);
  6. arrayList.add(64);
  7. System.out.println(arrayList);
  8. List<Number> arrayList1 = new ArrayList(arrayList);//把arraylist的大小作为参数传参
  9. arrayList1.add(99999999);//也有自己的add方法
  10. System.out.println(arrayList1);//打印输出[99, 88, 75, 64, 99999999],"把另一个集合 拿过来 构造当前的集合"
  11. //ArrayList<String> arrayList2 = new ArrayList<>(arrayList);//error
  12. /**
  13. 满足ArrayList实现了Collection接口的条件,但是不满足传入的参数是E类型或者E的子类类型().
  14. 传入的参数arrayList是Integer,和String无关系.既不是String类型也不是Stringent的子类类型
  15. */

ArrayList( Collection<? extends E> c)要同时满足两点:

  1. 顺序表要实现Collection接口
  2. 传入的参数是E类型本身 或者 E的子类类型 (? 表示通配符)
 ArrayList的subList()方法
  1. ArrayList<Integer> arrayList = new ArrayList<Integer>();
  2. arrayList.add(99);
  3. arrayList.add(88);
  4. arrayList.add(75);
  5. arrayList.add(64);
  6. System.out.println(arrayList);
  7. List<Number> arrayList1 = new ArrayList(arrayList);
  8. arrayList1.add(99999999);
  9. System.out.println(arrayList1);//打印输出[99, 88, 75, 64, 99999999]
  10. //subList截取
  11. List<Number> list =arrayList1.subList(0,2);//下标
  12. System.out.println(list);
  13. //[99, 88, 75, 64, 99999999]
  14. //我们认为截取后list的值是[99, 88]
  15. System.out.println("=======================");
  16. list.set(0,199);
  17. System.out.println(list);//把list里面0下标的值改为199,就认为变成[199,88]
  18. System.out.println(arrayList1);//arrayList1里面的值不变,还是[99, 88, 75, 64, 99999999]
  19. /**
  20. * 实际输出:
  21. * [199, 88] list中
  22. * [199, 88, 75, 64, 99999999] arrayList1
  23. * List<Number> list =arrayList1.subList(0,2);不是产生新的对象
  24. * list和arrayList1指向的是同一个对象
  25. * */

 

截取list中 [0,2)之间的元素 可以发现,arraylist1 和 list共用的是一个数组.list中发生改变时,arraylist1中的数据也会随之改变.

因此,截取方法不是产生一块新的对象,而是指向的同一个引用 .

ArrayList的遍历 
  1. public static void main3(String[] args) {
  2. //ArrayList的遍历
  3. ArrayList<Integer> arrayList = new ArrayList<>();
  4. arrayList.add(10);
  5. arrayList.add(20);
  6. arrayList.add(30);
  7. arrayList.add(40);
  8. //第一种 直接输出
  9. System.out.println(arrayList);
  10. //2种遍历方式 for
  11. for (int i = 0; i < arrayList.size(); i++) {
  12. System.out.print(arrayList.get(i) + " ");
  13. }
  14. System.out.println();
  15. //3、for-each
  16. for (int x: //自动装箱
  17. arrayList) {
  18. System.out.print(x + " ");
  19. }
  20. System.out.println();
  21. //4.迭代器(有多种使用方式)
  22. Iterator<Integer> it1 = arrayList.iterator();//iterator继承自Iterator
  23. while (it1.hasNext()) {
  24. System.out.print(it1.next() + " ");
  25. }
  26. System.out.println();
  27. //5
  28. Iterator<Integer> it2 = arrayList.listIterator();//listIterator继承自Iterator
  29. while (it2.hasNext()) {
  30. System.out.print(it2.next() + " ");
  31. }
  32. System.out.println();
  33. //6.从后向前遍历
  34. ListIterator<Integer> it3 = arrayList.listIterator(arrayList.size());
  35. //arrayList.size()是一个方法调用,它返回了arrayList列表的大小或元素数量。
  36. // 在这里,ListIterator<Integer> it3 = arrayList.listIterator(arrayList.size())的作用是
  37. // 创建一个ListIterator对象it3,并将其定位在arrayList列表的 末尾 位置。
  38. while (it3.hasPrevious()) {
  39. System.out.print(it3.previous() + " ");
  40. }
  41. System.out.println();
  42. }
深度学习 ArrayList的无参构造

ArrayList的add()方法  

通过观察ArrayList的源码 ,发现:

总结:

  1. 当我们进行ArrayList()的无参构造时,初始大小为0. 当我们第一次add(存放数据元素)时,会分配大小为10给数组,而当10个放满的时候,进行1.5倍扩容。如果调用的是给定容量的构造方法(括号里面带参数),那么顺序表的大小,就是你指定的容量,放满了之后也是1.5倍的扩容。
  2. 检测是否真正需要扩容,如果是调用grow准备扩容
  3. 使用copyOf进行扩容


寄语:不要忘了你希望的远方 

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

闽ICP备14008679号