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

打印 上一主题 下一主题

主题 980|帖子 980|积分 2940

找往期文章包括但不限于本期文章中不懂的知识点:
   个人主页:我要学编程(ಥ_ಥ)-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)尾插evoid add(int index, E element)将 e 插入到 index 位置boolean addAll(Collection<? extends E> c)将c 中的元素举行尾插E remove(int index)删除 index 位置元素boolean remove(Object o)删除碰到的第一个oE get(int index)获取下标index位置元素E set(int index, E element)将下标 index 位置元素改为 elementvoid 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. }
  15.         
复制代码
只要是实现了 Iterator 接口就可以利用迭代器的方式来遍历。就是下面这张图:

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立聪堂德州十三局店

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表