当前位置:   article > 正文

ArrayList与顺序表

ArrayList与顺序表

目录:

 一.线性表 

二.顺序表

三.ArrayList的简介
    四.ArrayList自我实现和使用
    五.ArrayList的扩容机制
    六.简易的洗牌算法

 一.线性表 : 

1.线性表(linear list)是n个 具有相同特性的数据元素的有限序列 。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在 物理结构上 不一定是连续 的,线性表在 物理上存储时 ,通常以 数组和链式 结构的形式存储。
二.顺序表:
顺序表是用一段物理地址连续的存储单元,依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。顺序表底层就是数组。学习时可以画图理解学习。

三.ArrayList的简介 :

1.在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

 2. ArrayList是以泛型方式实现的使用时必须要先实例化 ,ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问,

ArrayList实现了Cloneable接口,表明ArrayList是可以clone
ArrayList实现了Serializable接口,表明ArrayList是支持序列化的 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者 CopyOnWriteArrayList
ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

 图我们可以看出ArrayList,继承了很多类,并且扩展了很多接口使用,自身也有很多方法。

  四.ArrayList自我实现和使用(这里我们没有用泛型类实现):

我们可以仿照源码自己去实现,这里我们实现几个常见的方法,(注意ArrayList不止这些方法),自己的ArrayList

下面先来说明一下,自己模仿的每一个类的分配如图:

1.这里是IList接口里的方法:

  1. package listtest;
  2. /**
  3. * Created with IntelliJ IDEA.
  4. * Description:
  5. * User: 苏
  6. * Date: 2024-06-09
  7. * Time: 20:23
  8. */
  9. public interface IList {
  10. //判满
  11. public boolean isFull();
  12. // 新增元素,默认在数组最后新增
  13. public void add(int data);
  14. // 在 pos 位置新增元素
  15. public void add(int pos, int data);
  16. // 判定是否包含某个元素
  17. public boolean contains(int toFind);
  18. // 查找某个元素对应的位置
  19. public int indexOf(int toFind);
  20. // 获取 pos 位置的元素
  21. public int get(int pos);
  22. // 给 pos 位置的元素设为 value
  23. public void set(int pos, int value);
  24. //删除第一次出现的关键字key
  25. public void remove(int toRemove);
  26. // 获取顺序表长度
  27. public int size();
  28. // 清空顺序表
  29. public void clear();
  30. // 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
  31. public void display();
  32. }

2.这里是继承了My_ArrayList的类,具体方法构架界面:这里注意:要在某位置插入,这个位置的,前驱一定不能为空

  1. package listtest;
  2. import java.util.Arrays;
  3. /**
  4. * Created with IntelliJ IDEA.
  5. * Description:
  6. * User: 苏
  7. * Date: 2024-06-09
  8. * Time: 20:10
  9. */
  10. public class My_ArrayList implements IList {
  11. public int[] array;
  12. public int usedSize;//大小默认就是0,数组的有效长度
  13. public static final int DEFAULT_CAPACITY = 10;//数组的默认长度
  14. public My_ArrayList() {
  15. this.array = new int[DEFAULT_CAPACITY];
  16. }
  17. public boolean isFull() {
  18. return this.usedSize == this.array.length; //满了
  19. }
  20. //扩容
  21. private void grow() {
  22. //2倍扩容
  23. this.array = Arrays.copyOf(this.array, 2*this.array.length);
  24. }
  25. //检查pos位置下标,是否合法
  26. private void checkPosOf(int pos) throws IllegalPos {
  27. if (pos < 0 || pos > this.usedSize) {
  28. throw new IllegalPos("pos位置不合法");
  29. }
  30. }
  31. @Override
  32. public void add(int data) {
  33. if (isFull()) {
  34. //扩容
  35. grow();
  36. }
  37. this.array[this.usedSize++] = data;
  38. }
  39. @Override
  40. public void add(int pos, int data) {
  41. try {
  42. checkPosOf(pos);
  43. if (isFull()) {
  44. //扩容
  45. grow();
  46. }
  47. /**注意以下:
  48. * 要在某位置插入,这个位置的,前驱一定不能为空
  49. */
  50. //挪动元素
  51. for (int i = 0; i >= pos ; i--) {
  52. this.array[i+1] = this.array[i];
  53. }
  54. //插入
  55. this.array[pos] = data;
  56. usedSize++;
  57. }catch (IllegalPos e) {
  58. e.printStackTrace();
  59. }
  60. }
  61. @Override
  62. public boolean contains(int toFind) {
  63. for (int i = 0; i < usedSize; i++) {
  64. //这里如果是自己,定义的引用类型,那么要用equal比较
  65. if (array[i] == toFind) {
  66. return true;
  67. }
  68. }
  69. return false;
  70. }
  71. @Override
  72. public int indexOf(int toFind) {
  73. for (int i = 0; i < usedSize; i++) {
  74. //这里如果是自己,定义的引用类型,那么要用equal比较
  75. if (array[i] == toFind) {
  76. return i;
  77. }
  78. }
  79. return -1;
  80. }
  81. private void checkPosOf2(int pos) throws IllegalPos {
  82. if (pos < 0 || pos >= this.usedSize) {
  83. throw new IllegalPos("pos位置不合法");
  84. }
  85. }
  86. //顺序表判空
  87. public boolean isEmpty() {
  88. return this.usedSize == 0;
  89. }
  90. private void checkOfList() {
  91. if (this.isEmpty()) {
  92. throw new EmptyException("顺序表为空");
  93. }
  94. }
  95. @Override
  96. public int get(int pos) {
  97. try {
  98. //顺序表本来就为空时
  99. this.checkOfList();
  100. checkPosOf2(pos);
  101. return array[pos];
  102. }catch (IllegalPos e) {
  103. e.printStackTrace();
  104. }catch (EmptyException e) {
  105. e.printStackTrace();
  106. }
  107. return -1;
  108. }
  109. //更新
  110. @Override
  111. public void set(int pos, int value) {
  112. try {
  113. //顺序表本来就为空时
  114. this.checkOfList();
  115. checkPosOf2(pos);
  116. array[pos] = value;
  117. }catch (IllegalPos e) {
  118. e.printStackTrace();
  119. }catch (EmptyException e) {
  120. e.printStackTrace();
  121. }
  122. }
  123. @Override
  124. public void remove(int toRemove) {
  125. try {
  126. //判空
  127. this.checkOfList();
  128. //找到要删除的下标
  129. int pos = this.indexOf(toRemove);
  130. if (pos == -1) {
  131. System.out.println("找不到你要删除的元素");
  132. return;
  133. }
  134. for (int i = pos; i < usedSize-1/*这里注意,因为是array[i] = array[i+1]*/; i++) {
  135. this.array[i] = array[i+1];
  136. }
  137. this.usedSize--;
  138. }catch (EmptyException e) {
  139. e.printStackTrace();
  140. }
  141. }
  142. @Override
  143. public int size() {
  144. return this.usedSize;
  145. }
  146. @Override
  147. public void clear() {
  148. //如果是引用类型,要把,引用置为空
  149. /*for (int i = 0; i < this.usedSize; i++) {
  150. this.array[i] = null;
  151. }
  152. this.usedSize = 0;*/
  153. //基本类型直接这样写
  154. this.usedSize = 0;
  155. }
  156. @Override
  157. public void display() {
  158. for (int i = 0; i < usedSize; i++) {
  159. System.out.print(array[i] +" ");
  160. }
  161. }
  162. }

3.这里是两个自定义异常EmptyException(检查顺序表是否为空)和IllegalPos(检查下标是否合法):

  1. package listtest;
  2. /**
  3. * Created with IntelliJ IDEA.
  4. * Description:
  5. * User: 苏
  6. * Date: 2024-06-09
  7. * Time: 22:41
  8. */
  9. public class IllegalPos extends RuntimeException {
  10. public IllegalPos() {
  11. super();
  12. }
  13. public IllegalPos(String msg) {
  14. super(msg);
  15. }
  16. }

  1. package listtest;
  2. /**
  3. * Created with IntelliJ IDEA.
  4. * Description:
  5. * User: 苏
  6. * Date: 2024-06-10
  7. * Time: 12:22
  8. */
  9. public class EmptyException extends RuntimeException {
  10. public EmptyException() {
  11. super();
  12. }
  13. public EmptyException(String msg) {
  14. super(msg);
  15. }
  16. }

使用注意一:使用时ArrayList不传参数,他会Add给你分配内存,可以传另一个定义的顺序表(list)不过需要是,该泛型或泛型子类

 

 

 

使用注意二:ArrayList的遍历:

ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器 

1.for循环+下标遍历:

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

 

2.foreach遍历:

 

  1. public static void main(String[] args) {
  2. ArrayList<Integer> list = new ArrayList<>();
  3. list.add(1);
  4. list.add(2);
  5. list.add(3);
  6. list.add(4);
  7. //for each循环打印
  8. for(Integer x/*每一个元素的类型*/: list/*对象引用类型*/) {
  9. System.out.print(x +" ");
  10. }
  11. }

3.使用迭代器遍历:

  1. public static void main(String[] args) {
  2. ArrayList<Integer> list = new ArrayList<>();
  3. list.add(1);
  4. list.add(2);
  5. list.add(3);
  6. list.add(4);
  7. //使用迭代器输出
  8. Iterator<Integer> it = list.iterator();
  9. while (it.hasNext()) {
  10. System.out.print(it.next() + " ");
  11. }
  12. }

    五.ArrayList的扩容机制(略): 有兴趣可以去看看源码,jdk17被一层一层封装看起来麻烦,可以看jdk8

1. 检测是否真正需要扩容,如果是调用grow准备扩容
2. 预估需要库容的大小,初步 预估按照 1.5倍 大小扩容
如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
3.真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
4. 使用copyOf进行扩容

  六.简易的洗牌算法:

1.学了ArrayLisy之后,来看一下其应用,这里的洗牌算法,会用集合来定义二维数组,来把牌分别放到不同玩家手中,接下来有我娓娓到来:

 2.老规矩,来看一下,定义的,类的结构:

3.具体实现:

Card类,定义基本的对象和构造方法:

  1. package card;
  2. /**
  3. * Created with IntelliJ IDEA.
  4. * Description:
  5. * User: 苏李涛
  6. * Date: 2024-06-13
  7. * Time: 20:45
  8. */
  9. public class Card {
  10. public int rank;//牌的面值
  11. public String suit;//牌的花色
  12. public Card(int rank, String suit) {
  13. this.rank = rank;
  14. this.suit = suit;
  15. }
  16. @Override
  17. public String toString() {
  18. /*return "Card{" +
  19. "rank=" + rank +
  20. ", suit='" + suit + '\'' +
  21. '}';*/
  22. return "{" + suit + rank + "}";
  23. }
  24. }

cardDemo类定义,买牌,洗牌,揭牌方法具体如下: 

(1)买52张牌:这里注意要,声明Card对象,来通过构造方法来,初始化,每一张牌,每一张牌又有四种花色

  1. public class CardDemo {
  2. public static final String[] suits = new String[]{"♥", "♠", "♣", "♦"};
  3. //1.买52张牌
  4. public List<Card> buyCard() {
  5. List<Card> cardList = new ArrayList<>();
  6. //1.买52张牌
  7. for (int i = 1; i <= 13; i++) {
  8. for (int j = 0; j < 4; j++) {
  9. //赋值,增加代码可读
  10. String suit = suits[j];
  11. int rank = i;
  12. //初始化构造方法
  13. Card card = new Card(rank, suit);
  14. cardList.add(card);
  15. }
  16. }
  17. return cardList;
  18. }

(2)洗牌:这里巧妙,利用生产随机数,这里也要注意,交换牌时的Swap方法,通过集合来交换会有一个开区间下标不在生成随机数的范围内,通过这来打乱牌,我画了一个图来理解:

  1. //2.洗牌
  2. /**
  3. * 生成随机数,区间也是"[)"这样的,
  4. * 所以我们用 i 值,的前面生成随机数,然后和i位置交换
  5. * @return
  6. */
  7. public List<Card> shuffle(List<Card> cardList ) {
  8. Random random = new Random();
  9. for (int i = cardList.size()-1; i>0; i--) {
  10. int pre = random.nextInt(i);
  11. //这里会有,开区间数,index,这个数每次和随机数交换
  12. //i是下面index的值,也就是开区间的值
  13. swap(cardList, pre, i);
  14. }
  15. return cardList;
  16. }
  17. private void swap(List<Card> cardList, int i/*这个i是生产随机数值的,下标*/, int index/*是开区间下标*/) {
  18. /**注意不能注意写:因为cardList不是数组,是集合里面有数组
  19. *
  20. * cardList tmp = cardList[i]
  21. * index就是,生成随机数的,开区间下标 i
  22. * cardList[i] = cardList[index];
  23. * cardList[index] = tmp
  24. */
  25. //这个tmp就是,cardList.get(i)里元素的类型
  26. Card tmp = cardList.get(i);
  27. //把生成随机数下标的值,换成,随机数,开区间下标的值
  28. cardList.set(i, cardList.get(index));
  29. cardList.set(index, tmp);
  30. }

揭牌:这里注意:定义了一个二维数组,存放玩家揭的牌,也要注意,删除是以集合中覆盖的形式,如图:

  1. //3.揭牌
  2. public List<List<Card>> playCard(List<Card> cardList) {
  3. List<List<Card>> ret = new ArrayList<>();//存放一维数组的二维数组
  4. //下面是三个存放玩家,抽的牌,的一维数组
  5. List<Card> hand0 = new ArrayList<>();
  6. List<Card> hand1 = new ArrayList<>();
  7. List<Card> hand2 = new ArrayList<>();
  8. ret.add(hand0);
  9. ret.add(hand1);
  10. ret.add(hand2);
  11. //每个人每次,抽一张,每人抽5张
  12. for (int i = 0; i < 5; i++) {
  13. for (int j = 0; j < 3; j++) {
  14. //从上往下抽,一个删除一个,放到一维数组中,用二维数( List<List<Card>>)组访问。
  15. /**
  16. * 注意这里的删除,应该是,顺序表中remove,这个删除是以覆盖的形式
  17. */
  18. //删除扑克牌对象
  19. Card card = cardList.remove(0);//从0位置开始覆盖
  20. //把删除(覆盖)的扑克牌,放到二维数组中
  21. ret.get(j).add(card);
  22. }
  23. }
  24. return ret;//返回二维数组,用二维数组访问一维数组
  25. }

CardTest测试类:

  1. import card.Card;
  2. import card.CardDemo;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. /**
  6. * Created with IntelliJ IDEA.
  7. * Description:
  8. * User: 苏李涛
  9. * Date: 2024-06-13
  10. * Time: 20:45
  11. */
  12. public class CardTest {
  13. public static void main(String[] args) {
  14. //1.买52张牌
  15. CardDemo cardDemo = new CardDemo();
  16. //调用返回值为List<Card>的,泛型方法
  17. List<Card> cardList = cardDemo.buyCard();
  18. System.out.println(cardList);
  19. System.out.println();
  20. System.out.println();
  21. //2.洗牌
  22. List<Card> cardList1 = cardDemo.shuffle(cardList);
  23. System.out.println(cardList1);
  24. System.out.println();
  25. System.out.println();
  26. //3.揭牌
  27. //注意:揭牌的删除是,顺序表里的覆盖
  28. List<List<Card>> cardList2 = cardDemo.playCard(cardList);
  29. for (int i = 0; i < cardList2.size(); i++) {
  30. System.out.println("玩家"+ (i+1) + "的牌: " + cardList2.get(i));
  31. }
  32. }
  33. }

来看一下输出:

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

闽ICP备14008679号