当前位置:   article > 正文

【数据结构】ArrayList与顺序表_public class seqlist { static final int listsiz

public class seqlist { static final int listsize=100; private int length;

目录

1.List接口

2.线性表

3.顺序表

3.1常用方法 

3.2常用方法的实现

4.ArrayList

4.1构造方法

4.2遍历

4.3扩容

4.4CVTE面试题:删除相同字符

5.ArrayList的具体实现

5.1洗牌算法

5.2杨辉三角

6.ArrayList的优缺点


1.List接口

List 接口继承于 Collection 接口,Collection 接口继承于 Iterable 接口。

这三个接口中都有许多常用方法(点击Structure可以查看)。

  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Stack;
  5. public class Test {
  6. List<Integer> stack = new Stack<>();//栈
  7. List<Integer> arrayList = new ArrayList<>();//顺序表
  8. List<Integer> linkList = new LinkedList<>();//链表
  9. }

2.线性表

定义:n个具有相同特性的数据元素的有限序列,是一种在实际中广泛使用的数据结构。

常见的线性表:顺序表、链表、栈、队列……

逻辑上是线性结构,即连续的一条直线,但在物理结构上并不一定是连续的。

物理上存储时,通常以数组和链式结构的形式存储。

3.顺序表

定义:用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。逻辑上是连续的,物理上也是连续的。

即:通过方法操作数组

3.1常用方法 

方法说明
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 )判断 o 是否在线性表中
int indexOf ( Object o )返回第一个 o 所在的下标
int lastIndexOf ( Object o )返回最后一个 o 的下标
List<E> subList ( int fromIndex, int toIndex )截取部分List

 其中E为泛型。

注意subList 方法截取的是原来List对象中的地址,所以修改subList截取后的值会同时改变原来List中的值。

3.2常用方法的实现

下面方法ArrayList表中都有,我们自己来实现一下,更能理解里面具体的实现原理: 

  1. package Demo1;
  2. public class PosOutOfBoundsException extends RuntimeException{
  3. public PosOutOfBoundsException(String message) {
  4. super(message);
  5. }
  6. }
  1. package Demo1;
  2. public class Test {
  3. public static void main(String[] args) {
  4. SeqList seqList = new SeqList();
  5. for (int i = 0; i < 10; i++) {
  6. seqList.add(i,i);
  7. }
  8. seqList.display();
  9. //System.out.println(seqList.get(6));//报错
  10. //……………………
  11. }
  12. }
  1. package Demo1;
  2. import java.util.Arrays;
  3. public class SeqList {
  4. private int[] elem;
  5. private int usedSize;//默认为0,记录当前顺序表中有几个有效的数据
  6. private static final int DEFAULT_CAPACITY = 3;//默认容量
  7. public SeqList() { //不带参数的构造方法
  8. this.elem = new int[DEFAULT_CAPACITY];//new一个数组
  9. }
  10. private boolean isFull() { //判断数组是否满了
  11. return this.usedSize == this.elem.length;
  12. }
  13. private boolean checkPos(int pos) { //判断获取的下标是否合法
  14. if (pos < 0 || pos >= this.usedSize) {
  15. return false;
  16. }
  17. return true;
  18. }
  19. private boolean isEmpty() { //判断顺序表是否为空数据
  20. return this.usedSize == 0;
  21. }
  22. private void resize() { //扩容
  23. this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
  24. //copyOf开辟一个长度为2*elem.length的新数组并将原elem数组中的元素拷贝进去,再返回给elem
  25. }
  26. public void display() { //打印顺序表
  27. for (int i = 0; i < this.usedSize; i++) {
  28. System.out.println(this.elem[i]+" ");
  29. }
  30. System.out.println();
  31. }
  32. public void add(int data) { //新增元素,默认在已有数据后新增
  33. if (isFull()) { //如果满了,给数组扩容
  34. resize();
  35. }
  36. this.elem[this.usedSize] = data;
  37. this.usedSize++;
  38. }
  39. public void add(int pos,int data) { //在pos位置新增元素data
  40. if (isFull()) { //满了就扩容
  41. resize();
  42. }
  43. if (pos < 0 || pos > this.usedSize) { //注意这里与checkPos不同
  44. throw new PosOutOfBoundsException("add时位置不合法");
  45. }
  46. for (int i = this.usedSize-1; i >= pos ; i--) { //给data挪位置
  47. this.elem[i+1] = this.elem[i];
  48. }
  49. this.elem[pos] = data;//存data
  50. this.usedSize++;
  51. }
  52. public boolean contains(int toFind) { //判断是否包含某个元素
  53. for (int i = 0; i < this.usedSize; i++) {
  54. if (toFind == this.elem[i]) {
  55. return true;
  56. }
  57. }
  58. return false;
  59. }
  60. public int indexOf(int toFind) { //查找某个元素对应位置的下标
  61. for (int i = 0; i < this.usedSize; i++) {
  62. if (toFind == this.elem[i]) {
  63. return i;
  64. }
  65. }
  66. return -1;
  67. }
  68. public int get(int pos) { //获取pos位置处的元素
  69. if (checkPos(pos) == false) {
  70. throw new PosOutOfBoundsException("get时位置不合法");//自定义一个异常
  71. }
  72. return this.elem[pos];
  73. }
  74. public int size() { //获取当前顺序表长度
  75. return this.usedSize;
  76. }
  77. public void set(int pos,int value) { //将pos位置处的值更新为value
  78. if (checkPos(pos)) {
  79. throw new PosOutOfBoundsException("set时位置不合法");
  80. }
  81. this.elem[pos] = value;
  82. }
  83. public void remove(int key) { //删除第一次出现的关键字key
  84. if (isEmpty()) {
  85. return;
  86. }
  87. int index = indexOf(key); //查找key
  88. if (index == -1) { //没找到要删除的key
  89. return;
  90. }
  91. for (int i = index; i <this.usedSize-1; i++) {
  92. this.elem[i] = this.elem[i+1];
  93. }
  94. this.usedSize--;//这一步不能忘!
  95. //this.elem[usedSize] = null; 存储引用类型时要手动置空
  96. }
  97. public void clear() { //清空顺序表
  98. /*for (int i = 0; i < usedSize; i++) {
  99. this.elem[i] = null;
  100. }*/ //此处是当elem中存储引用类型数据时,清空时全部置为null
  101. this.usedSize = 0; //存数字时只需将有效元素个数置为0即可
  102. }
  103. }

4.ArrayList

注意:1> ArrayList实现了List接口;

2> ArrayList是以泛型方式实现的,使用时必须要先实例化;

3> 实现了RandomAccess接口,表明支持随机访问

4> 实现了Cloneable接口,表明支持克隆

5> 实现了Serializable接口,表明支持序列化

6> ArrayList不是线程安全的,在单线程下可以使用,多线程中应选择 VectorCopyOnWriteArrayList

7> ArrayList底层是一段连续的空间,可以动态扩容,是一个动态类型的顺序表。

4.1构造方法

方法说明
ArrayList ()无参构造
ArrayList ( Collection < ? extends E > c )利用其他Collection构建ArrayList
ArrayList ( int intialCapacity )指定顺序表初始容量

注意:上述第二个构造方法中,?是通配符,在之后的泛型进阶中会讲到;后面括号中代码意为参数必须为E本身或E的子类。 

  1. public class Test {
  2. public static void main(String[] args) {
  3. List<Integer> list1 = new ArrayList<>();//构造一个空的顺序表
  4. List<Integer> list2 = new ArrayList<>(10);//构造一个初始容量为10的顺序表
  5. list2.add(1);
  6. list2.add(2);
  7. list2.add(3);
  8. ArrayList<Integer> list3 = new ArrayList<>(list2);//参数为本身或其子类时,可以直接赋值
  9. }
  10. }

4.2遍历

四种遍历方法:sout直接打印,for循环,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. //法一:sout直接输出
  8. System.out.println(arrayList);//[1, 2, 3]---原因:父类中重写了toString方法
  9. //法二:for循环
  10. for (int i = 0; i < arrayList.size(); i++) {
  11. System.out.print(arrayList.get(i)+" ");
  12. }
  13. System.out.println();
  14. //法三:for-each循环
  15. for (int x : arrayList) { //冒号前为类型和临时创建的变量,冒号后为需要遍历的对象
  16. System.out.print(x+" ");
  17. }
  18. System.out.println();
  19. //法四:迭代器(设计模式的一种)
  20. Iterator<Integer> iterator = arrayList.listIterator();//定义一个迭代器
  21. while (iterator.hasNext()) { //如果有下一个就打印下一个
  22. System.out.print(iterator.next()+" ");
  23. }
  24. System.out.println();
  25. }
  26. }

4.3扩容

源码见下:

说明

1.当调用不带参数的构造方法时,底层的数组长度为0(空数组);

2.第一次add时,会给我分配大小为10的内存;

3.所需空间超过原有空间时,初步预估按照1.5倍大小扩容;

4.如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容;

5. 使用copyOf进行扩容。

注意

1. 检测是否真正需要扩容,如果是调用grow准备扩容;

2.扩容之前检测是否能扩容成功,防止太大导致扩容失败。

4.4CVTE面试题:删除相同字符

编程要求:删除第一个字符串中出现的第二个字符串中的字符。 

  1. public class Test {
  2. //法一:ArrayList
  3. public static void func1(String str1,String str2) {
  4. List<Character> ret = new ArrayList<>();//定义一个ArrayList表用来存放结果
  5. for (int i = 0; i < str1.length(); i++) {
  6. char ch = str1.charAt(i);//遍历获取str1中的每个字符
  7. if (!str2.contains(ch+"")) { //如果str2不包含这个字符
  8. ret.add(ch);//将这个字符插入顺序表中
  9. }
  10. }
  11. for (int i = 0; i < ret.size(); i++) {
  12. System.out.print(ret.get(i));
  13. }
  14. }
  15. //法二:数组
  16. public static String func1(String str1,String str2) {
  17. String Tmp = str1;
  18. for (int i = 0; i < str2.length(); i++) {
  19. String tmp = String.valueOf(str2.charAt(i));
  20. Tmp = Tmp.replaceAll(tmp,"");
  21. }
  22. return Tmp;
  23. }
  24. public static void main(String[] args) {
  25. func("welcome to world","come");//wl t wrld
  26. String s = func1("welcome to world","come");
  27. System.out.println(s);//wl t wrld
  28. }
  29. }

这里有一个编程技巧:因为 contains 的参数为 CharSequence,是字符串,而ch只是一个字符,此时将ch 加上一个"" 即可将字符变为字符串。

5.ArrayList的具体实现

5.1洗牌算法

Card.java

  1. package Demo;
  2. //这里面的所有代码都可以用Generate自动生成
  3. public class Card { //定义牌
  4. private String suit;//花色
  5. private int rank;//数字
  6. public Card(String suit, int rank) {
  7. this.suit = suit;
  8. this.rank = rank;
  9. }
  10. public String getSuit() {
  11. return suit;
  12. }
  13. public void setSuit(String suit) {
  14. this.suit = suit;
  15. }
  16. public int getRank() {
  17. return rank;
  18. }
  19. public void setRank(int rank) {
  20. this.rank = rank;
  21. }
  22. @Override
  23. public String toString() {
  24. return suit+rank ;
  25. }
  26. }

Test.java 

  1. package Demo;
  2. import java.util.*;
  3. public class Test {
  4. private static final String[] SUITS = {"♥","♦","♠","♣"};//定义四个花色
  5. public static List<Card> buyCard() { //买牌(初始化牌)
  6. List<Card> cards = new ArrayList<>();//定义一个cards表
  7. for (int i = 0; i < SUITS.length; i++) {
  8. for (int j = 1; j <= 13; j++) {
  9. Card card = new Card(SUITS[i],j);//调用了Card的构造方法,分别new了一张牌
  10. cards.add(card);//将牌放入cards表里
  11. }
  12. }
  13. return cards;
  14. }
  15. public static void shuffle(List<Card> cards) { //洗牌
  16. //Collections.shuffle(cards); --- 库方法中有,这里我们自己实现:
  17. Random random = new Random();//new一个随机数对象
  18. for (int i = cards.size()-1; i > 0 ; i--) {
  19. int j = random.nextInt(i);//随机取一个[0,i)中的值(下标)
  20. Card tmp = cards.get(i);//在表中随机取一个值
  21. cards.set(i,cards.get(j));//利用set方法交换这两个值
  22. cards.set(j,tmp);
  23. }
  24. }
  25. public static void main(String[] args) {
  26. List<Card> cards = buyCard();//买牌(初始化牌)
  27. System.out.println("初始的牌:"+cards);
  28. shuffle(cards);//洗牌
  29. System.out.println("洗完的牌:"+cards);
  30. //定义三个表分别存放每个人揭的牌
  31. List<Card> hand1 = new ArrayList<>();
  32. List<Card> hand2 = new ArrayList<>();
  33. List<Card> hand3 = new ArrayList<>();
  34. //再定义一个表存放上面存牌的三个表(相当于二维数组)
  35. List<List<Card>> hand = new ArrayList<>();//注意这里List<>中的类型
  36. hand.add(hand1);
  37. hand.add(hand2);
  38. hand.add(hand3);
  39. //三个人轮流揭5张牌,注意不是一次性揭5张牌
  40. for (int i = 0; i < 5; i++) {
  41. for (int j = 0; j < 3; j++) {
  42. Card card = cards.remove(0);//揭牌,即每次拿走表中的第一张(0下标处的)牌
  43. hand.get(j).add(card);//相当于先拿到二维数组的行数,再去访问行里的元素
  44. }
  45. }
  46. System.out.println("第一个人的牌:"+hand1);
  47. System.out.println("第二个人的牌:"+hand2);
  48. System.out.println("第三个人的牌:"+hand3);
  49. System.out.println("剩下的牌"+cards);
  50. }
  51. }

5.2杨辉三角

杨辉三角

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. public class Test {
  4. public static List<List<Integer>> generate(int numRows) { //这个类型相当于二维数组
  5. List<List<Integer>> ret = new ArrayList<>();//用来接收整个杨辉三角,相当于二维数组
  6. List<Integer> list = new ArrayList<>();//用来接收杨辉三角的每一行,相当于一维数组
  7. list.add(1);//第一行只有1
  8. ret.add(list);//将第一行放入"二维数组"中
  9. for (int i = 1; i < numRows; i++) { //循环add每一行
  10. List<Integer> curRow = new ArrayList<>();
  11. curRow.add(1);//每一行第一个元素均为1
  12. for (int j = 1; j < i; j++) { //这里add每一行中间的元素
  13. int value = ret.get(i-1).get(j)+ret.get(i-1).get(j-1);//!!!
  14. curRow.add(value);
  15. }
  16. curRow.add(1);//每一行最后一个元素均为1
  17. ret.add(curRow);//add完一行后再整行add进"二维数组"中
  18. }
  19. return ret;
  20. }
  21. public static void main(String[] args) {
  22. List<List<Integer>> list = new ArrayList<>();
  23. list = generate(5);
  24. System.out.println(list);
  25. }
  26. }

6.ArrayList的优缺点

优点:根据指定下标 (索引) 去查找元素或更新元素的效率非常高,时间复杂度可为O(1)。

缺点:1> 每次插入或删除数据时,都需要移动数据,若要插到0下标位置或删除0下标数据,复杂度将达到O(n);2> 扩容时默认扩容1.5倍,当原空间很大而扩容空间只使用了一点时,将会浪费很多空间。

总结:顺序表适用于经常进行查找元素或更新元素的场景下使用,在另一些顺序表不适用的场景下,就需要 链表 出马了~~


下一篇文章就要探讨链表的奥秘了,狠狠期待住了!!

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

闽ICP备14008679号