Java中List、ArrayList与顺序表

List、ArrayList与顺序表

  • List
    • 什么是List
    • 常用方法介绍
    • List的使用
  • ArrayList与顺序表
    • 线性表
    • 顺序表
      • 接口的实现
    • ArrayList简介
    • ArrayList的使用
      • ArrayList的构造
      • ArrayList的常见操作
      • ArrayList的遍历
      • ArrayList的扩容机制
    • ArrayList的具体使用
      • 杨辉三角
      • 简单的洗牌算法
    • ArrayList的问题及思考

List

什么是List

在集合框架中,List是一个接口,继承自Collection。
在这里插入图片描述
Collection 也是一个接口,该接口中规范了后序容器中常用的一些方法,具体如下所示:
在这里插入图片描述
Iterable 也是一个接口,表示实现该接口的类是可以逐个元素进行遍历的,具体如下:
在这里插入图片描述
List的官方文档
在数据结构的角度看,List就是一个线性表,即n个具有相同类型元素的有限序列,在该序列上可以进行增删查改以及变量等操作。

常用方法介绍

方法解释
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

List的使用

注意:List是一个接口,并不能直接用来实例化。
如果要使用,必须去实例化List的实现类。在集合框架中,ArrayList和LinkedList都实现了List接口。

ArrayList与顺序表

线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。线性表实际上是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列…
线性表在逻辑上是线性结构,也就是说是一条连续的直线,但是在物理结构上并不是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
在这里插入图片描述

顺序表

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

接口的实现

我们通过模拟实现来进一步学习ArrayList

public interface IList {// 新增元素,默认在数组最后新增public void add(int data);// 在 pos 位置新增元素public void add(int pos, int data);// 判定是否包含某个元素public boolean contains(int toFind);// 查找某个元素对应的位置public int indexOf(int toFind);// 获取 pos 位置的元素public int get(int pos);// 给 pos 位置的元素设为 valuepublic void set(int pos, int value);//删除第一次出现的关键字keypublic void remove(int toRemove);// 获取顺序表长度public int size();// 清空顺序表public void clear();// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的public void display();
}
public class MyArrayList implements IList{public int[] array;public int usedSize;public static final int DEFAULT_CAPACITY = 5;public MyArrayList() {this.array = new int[DEFAULT_CAPACITY];}
}

接下来对接口中的方法进行重写
首先从最简单的打印顺序表和获取顺序表长度开始

@Override
public int size() {return this.usedSize;
}
@Override
public void display() {for (int i = 0; i < usedSize; i++) {System.out.print(array[i] + " ");}
}

接下来实现add(新增元素,默认在数组最后新增)
在实现之前,我们需要思考该数组会不会已经放满了,我们需要对其检查,若是放满我们还需要对其扩容,我们默认大小只有5,代码并不是简简单单的插入元素这么简单。

public boolean isFull(int[] array){return this.usedSize == array.length;
}private void grow(){this.array = Arrays.copyOf(this.array,this.array.length * 2);
}@Override
public void add(int data) {if(isFull(this.array)){grow();}array[usedSize] = data;usedSize++;
}

在 pos 位置新增元素,我们需要对pos及后面的元素往后移,从而空出位置来插入,我们需要从usedSize-1开始往后移,而不是pos,因为从pos往后会将后面的元素进行覆盖,从而丢失数据。还有我们依然需要对数组是否已经放满进行检查,而且需要对pos是否合法进行检查,负数是不可以的,超过数组usedSize是不可以插入的。usedSize这个位置是可以插入的,因为规定每次插入数据的位置,前驱必须存在,也就是说插入位置的前一个不能是空的。

public class PosIllegal extends RuntimeException{public PosIllegal() {}public PosIllegal(String msg) {super(msg);}
}
private void checkPosOfAdd(int pos) throws PosIllegal{if(pos < 0 || pos > this.usedSize){throw new PosIllegal("插入位置不合法");}
}
@Override
public void add(int pos, int data) {try{checkPosOfAdd(pos);if(isFull(this.array)){grow();}for (int i = usedSize - 1; i >= pos ; i--) {this.array[i + 1] = this.array[i];}array[pos] = data;usedSize++;}catch(PosIllegal e){e.printStackTrace();}
}

接下来实现判定是否包含某个元素和查找某个元素对应的位置的方法。

 @Overridepublic boolean contains(int toFind) {for (int i = 0; i < this.usedSize; i++) {if(array[i] == toFind){return true;}//如果存放的不是整型元素,而是引用类型的元素,则需要使用equals方法来比较,并且重写该方法。}return false;}@Overridepublic int indexOf(int toFind) {for (int i = 0; i < this.usedSize; i++) {if(array[i] == toFind){return i;}//如果存放的不是整型元素,而是引用类型的元素,则需要使用equals方法来比较,并且重写该方法。}return -1;}

获取 pos 位置的元素,对于获取元素,我们依然需要对pos位置是否合法进行检查,但是这一次pos位置不能小于0,而且不能大于usedSize -1,因为数组从0开始,usedSize不可能存放元素,对于获取元素我们还应该考虑一点,就是当数组为空时,我们是不能获取到任何元素的,所以我们需要对数组是否为空进行检查,当我们发现其为空时,我们需要抛出异常,因为无论我们返回何整数,都有可能在数组中存在,所以抛出异常是最好的。

public boolean isEmpty(){return this.usedSize == 0;
}private void checkEmpty(){if(isEmpty()){throw new EmptyException("顺序表为空");}
}private void checkPosOfGet(int pos){if(pos < 0 || pos > this.usedSize -1){throw new PosIllegal("获取元素的位置不合法");}
}@Override
public int get(int pos) {try{checkEmpty();checkPosOfGet(pos);return array[pos];}catch(PosIllegal e){e.printStackTrace();}catch(EmptyException e){e.printStackTrace();}return -1;
}

给 pos 位置的元素设为 value,这就是更新的意思,也就是与得到元素类似需要对其位置进行检查,不能不能小于0,而且不能大于usedSize -1,也要对数组是否为空进行检查。

@Override
public void set(int pos, int value) {try{checkEmpty();checkPosOfGet(pos);array[pos] = value;}catch(PosIllegal e){e.printStackTrace();}catch(EmptyException e){e.printStackTrace();}
}

删除第一次出现的关键字key,首先要对顺序表是否为空进行判断,空是没办法删除的。不为空之后我们可以通过遍历查找该元素的下标,找不到直接返回,找到对其后的元素进行挪动来覆盖,最后不要忘了usedSize进行减一。

 @Overridepublic void remove(int toRemove) {try{checkEmpty();int pos = indexOf(toRemove);if(pos == -1){return;}for (int i = pos; i < this.usedSize - 1; i++) {this.array[i] = this.array[i+1];}this.usedSize--;}catch(EmptyException e){e.printStackTrace();}}

清空顺序表,对于清空顺序表,在int数组中我们可以对其usedSize置为0,后面在add也只是覆盖,但是如果是引用类型,这样会造成内存泄漏,因为数组中依然有一段地址指向一个空间,而这个空间并没有什么作用,所以应该将其置为null。

@Override
public void clear() {this.usedSize = 0;/*for (int i = 0; i < this.usedSize; i++) {this.array[i] = null;}*/
}

到这里我们就将ArrayList中常用的方法模拟实现了。下面为完整代码和测试代码

public interface IList {// 新增元素,默认在数组最后新增public void add(int data);// 在 pos 位置新增元素public void add(int pos, int data);// 判定是否包含某个元素public boolean contains(int toFind);// 查找某个元素对应的位置public int indexOf(int toFind);// 获取 pos 位置的元素public int get(int pos);// 给 pos 位置的元素设为 valuepublic void set(int pos, int value);//删除第一次出现的关键字keypublic void remove(int toRemove);// 获取顺序表长度public int size();// 清空顺序表public void clear();// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的public void display();
}
public class PosIllegal extends RuntimeException{public PosIllegal() {}public PosIllegal(String msg) {super(msg);}
}
public class EmptyException extends RuntimeException{public EmptyException() {}public EmptyException(String message) {super(message);}
}
import java.util.Arrays;
public class MyArrayList implements IList{public int[] array;public int usedSize;public static final int DEFAULT_CAPACITY = 5;public MyArrayList() {this.array = new int[DEFAULT_CAPACITY];}public boolean isFull(int[] array){return this.usedSize == array.length;}private void grow(){this.array = Arrays.copyOf(this.array,this.array.length * 2);}@Overridepublic void add(int data) {if(isFull(this.array)){grow();}array[usedSize] = data;usedSize++;}private void checkPosOfAdd(int pos) throws PosIllegal{if(pos < 0 || pos > this.usedSize){throw new PosIllegal("插入位置不合法");}}@Overridepublic void add(int pos, int data) {try{checkPosOfAdd(pos);if(isFull(this.array)){grow();}for (int i = usedSize - 1; i >= pos ; i--) {this.array[i + 1] = this.array[i];}array[pos] = data;usedSize++;}catch(PosIllegal e){e.printStackTrace();}}@Overridepublic boolean contains(int toFind) {for (int i = 0; i < this.usedSize; i++) {if(array[i] == toFind){return true;}//如果存放的不是整型元素,而是引用类型的元素,则需要使用equals方法来比较,并且重写该方法。}return false;}@Overridepublic int indexOf(int toFind) {for (int i = 0; i < this.usedSize; i++) {if(array[i] == toFind){return i;}//如果存放的不是整型元素,而是引用类型的元素,则需要使用equals方法来比较,并且重写该方法。}return -1;}public boolean isEmpty(){return this.usedSize == 0;}private void checkEmpty(){if(isEmpty()){throw new EmptyException("顺序表为空");}}private void checkPosOfGet(int pos){if(pos < 0 || pos > this.usedSize -1){throw new PosIllegal("获取元素的位置不合法");}}@Overridepublic int get(int pos) {try{checkEmpty();checkPosOfGet(pos);return array[pos];}catch(PosIllegal e){e.printStackTrace();}catch(EmptyException e){e.printStackTrace();}return -1;}@Overridepublic void set(int pos, int value) {try{checkEmpty();checkPosOfGet(pos);array[pos] = value;}catch(PosIllegal e){e.printStackTrace();}catch(EmptyException e){e.printStackTrace();}}@Overridepublic void remove(int toRemove) {try{checkEmpty();int pos = indexOf(toRemove);if(pos == -1){return;}for (int i = pos; i < this.usedSize - 1; i++) {this.array[i] = this.array[i+1];}this.usedSize--;}catch(EmptyException e){e.printStackTrace();}}@Overridepublic int size() {return this.usedSize;}@Overridepublic void clear() {this.usedSize = 0;/*for (int i = 0; i < this.usedSize; i++) {this.array[i] = null;}*/}@Overridepublic void display() {for (int i = 0; i < usedSize; i++) {System.out.print(array[i] + " ");}System.out.println();}
}
public class Test {public static void main(String[] args) {MyArrayList list1 = new MyArrayList();IList list2 = new MyArrayList();System.out.println("初始有效元素个数:");System.out.println(list1.usedSize);System.out.println("打印初始顺序表");list1.display();System.out.println("打印初始数组大小");System.out.println(list1.size());list1.add(1);list1.add(2);list1.add(3);list1.add(4);System.out.println("打印插入元素后的顺序表");list1.display();System.out.println("打印插入元素后的顺序表大小");System.out.println(list1.size());list1.add(2,33);System.out.println("打印在指定位置插入元素后的顺序表");list1.display();//list1.add(44,4);System.out.println("顺序表是否包含某个元素");System.out.println(list1.contains(2));System.out.println(list1.contains(55));System.out.println("查找某个元素的指定位置");System.out.println(list1.indexOf(2));System.out.println(list1.indexOf(44));System.out.println("获取某个位置的元素");System.out.println(list1.get(1));//System.out.println(list1.get(100));System.out.println("更新某个位置的元素");list1.set(0,11);list1.display();System.out.println("删除第一次出现的关键字key");list1.remove(33);list1.display();System.out.println("清空顺序表");list1.clear();list1.display();//结果为://初始有效元素个数://0//打印初始顺序表////打印初始数组大小//0//打印插入元素后的顺序表//1 2 3 4 //打印插入元素后的顺序表大小//4//打印在指定位置插入元素后的顺序表//1 2 33 3 4 //顺序表是否包含某个元素//true//false//查找某个元素的指定位置//1//-1//获取某个位置的元素//2//更新某个位置的元素//11 2 33 3 4 //删除第一次出现的关键字key//11 2 3 4 //清空顺序表}
}

ArrayList简介

在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:
在这里插入图片描述
说明:

  1. ArrayList是以泛型的方式实现的,使用时必须要先实例化
  2. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
  3. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
  4. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
  5. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
  6. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表。

ArrayList的使用

ArrayList的构造

方法解释
ArrayList ()无参构造
ArrayList (Collection<? extends E> c)利用其他 Collection 构建 ArrayList
ArrayList (int initialCapacity)指定顺序表初始容量
public class Test {public static void main(String[] args) {//ArrayList创建//构造一个空的列表List<Integer> list1 = new ArrayList<>();//构造一个具有10个容量的列表List<Integer> list2 = new ArrayList<>(10);list2.add(1);list2.add(2);list2.add(3);//list2.add("hello");   //编译失败,List<Integer>本身就限定了list2中只能存储整型元素//list3构造好之后,与list中的元素一致ArrayList<Integer> list3 = new ArrayList<>(list2);//避免省略类型,否则:任意类型的元素都可以存放,使用时很麻烦List list4 = new ArrayList();list4.add(1);list4.add("hello");}
}

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)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List< E > subList(int fromIndex, int toIndex)截取部分 list
public class Test {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("food");list.add("book");list.add("clothes");list.add("drink");System.out.println(list);   //[food, book, clothes, drink]//获取list中有效元素的个数System.out.println(list.size());    //4//获取和设置index位置上的元素,注意index必须介于[0,size)间System.out.println(list.get(1));    //booklist.set(1,"BOOK");System.out.println(list.get(1));    //BOOK//在list的index位置插入指定元素,index及后续的元素统一往后搬移一个位置list.add(1,"shoes");System.out.println(list);   //[food, shoes, BOOK, clothes, drink]//删除指定元素,找到了就删除,该元素之后的元素统一往前搬移一个位置list.remove("shoes");System.out.println(list);   //[food, BOOK, clothes, drink]//删除list中的index位置上的元素,注意index不用超过list中有效元素个数,否则会抛出下标越界异常list.remove(list.size() - 1);   System.out.println(list);   //[food, BOOK, clothes]//检测list中是否包含指定元素,包含返回true,否则返回falseif(!list.contains("drink")){list.add("drink");}System.out.println(list);   //[food, BOOK, clothes, drink]//查找指定元素第一次出现的位置:indexOf从前往后找,lastIndexOf从后往前找list.add("bag");System.out.println(list);   //[food, BOOK, clothes, drink, bag]System.out.println(list.indexOf("bag"));    //4System.out.println(list.lastIndexOf("bag"));    //4//使用list中[0,4)之间的元素构成一个新的subList返回,但是和ArrayList共用一个elementData数组,//也就的引用指向同一个空间,当你修改subList中的元素,List指向的空间中的元素自然也改变了。List<String> ret = list.subList(0,4);System.out.println(ret);    //[food, BOOK, clothes, drink]System.out.println(list);   //[food, BOOK, clothes, drink, bag]ret.set(0,"FOOD");System.out.println(list);   //[FOOD, BOOK, clothes, drink, bag]System.out.println(ret);    //[FOOD, BOOK, clothes, drink]list.clear();System.out.println(list.size());    //0}
}

ArrayList的遍历

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

public class Test {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);System.out.println(list);System.out.println("=== for循环遍历 ===");for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i) + " ");}System.out.println();System.out.println("=== foreach遍历 ===");for (Integer x : list) {System.out.print(x + " ");}System.out.println();System.out.println("=== 使用迭代器Iterator输出 ===");Iterator<Integer> it1 = list.iterator();while(it1.hasNext()){System.out.print(it1.next() + " ");}System.out.println();System.out.println("=== 使用迭代器ListIterator输出 ===");ListIterator<Integer> it2 = list.listIterator();while(it2.hasNext()){System.out.print(it2.next() + " ");}System.out.println();System.out.println("=== 使用迭代器ListIterator输出 拓展 ===");ListIterator<Integer> it3 = list.listIterator(list.size());while(it3.hasPrevious()){System.out.print(it3.previous() + " ");}}//结果为://[1, 2, 3, 4]//=== for循环遍历 ===//1 2 3 4 //=== foreach遍历 ===//1 2 3 4 //=== 使用迭代器Iterator输出 ===//1 2 3 4 //=== 使用迭代器ListIterator输出 ===//1 2 3 4 //=== 使用迭代器ListIterator输出 拓展 ===//4 3 2 1 
}

ArrayList的扩容机制

ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容。以下是ArrayList源码中扩容方式:

Object[] elementData; // 存放元素的空间
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间
private static final int DEFAULT_CAPACITY = 10; // 默认容量大小
public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;return true;
}
private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {// 获取旧空间大小int oldCapacity = elementData.length;// 预计按照1.5倍方式扩容int newCapacity = oldCapacity + (oldCapacity >> 1);// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 调用copyOf扩容elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {// 如果minCapacity小于0,抛出OutOfMemoryError异常if (minCapacity < 0)throw new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

总结:

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

ArrayList的具体使用

杨辉三角

杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
在这里插入图片描述
示例 1:
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
在这里插入图片描述
在这里插入图片描述

public class Test{//List<List<Integer>> 为二维数组public static List<List<Integer>> generate(int numRows) {List<List<Integer>> ret = new ArrayList<>();List<Integer> list0 = new ArrayList<>();list0.add(1);ret.add(list0);for (int i = 1; i < numRows; i++) {List<Integer> curRow = new ArrayList<>();//处理第一个元素curRow.add(1);//中间for (int j = 1; j < i; j++) {Integer data = ret.get(i-1).get(j-1) + ret.get(i-1).get(j);curRow.add(data);}//尾部curRow.add(1);ret.add(curRow);}return ret;}public static void main(String[] args) {List<List<Integer>> ret = generate(4);/*System.out.println(ret);*/for (int i = 0; i < ret.size(); i++) {for (int j = 0; j < ret.get(i).size(); j++) {System.out.print(ret.get(i).get(j) + " ");}System.out.println();}//结果为://1 //1 1 //1 2 1 //1 3 3 1 }
}

简单的洗牌算法

要求:

  • 买52张牌
  • 洗牌
  • 3个人,每个人轮流拿五张
public class Card {public int rank;    //牌面值public String suit; //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}@Overridepublic String toString() {return "{" + rank + suit + '}';}
}
public class CardDemo {public static final String[] suits = {"♦","♣","♥","♠"};public List<Card> buyCard(){List<Card> cardList = new ArrayList<>(52);for (int i = 1; i <= 13 ; i++) {for (int j = 0; j < 4; j++) {int rank = i;String suit = suits[j];cardList.add(new Card(rank,suit));}}return cardList;}public void shuffle(List<Card> cardlist){Random random = new Random();for (int i = cardlist.size() - 1; i > 0; i--) {int index = random.nextInt(i);swap(cardlist,i,index);}}private void swap(List<Card> cardList, int i, int j){/*Card tmp = cardList[i];cardList[i] = cardList[j];cardList[j] = tmp;*/Card tmp = cardList.get(i);cardList.set(i,cardList.get(j));cardList.set(j,tmp);}public List<List<Card>> play(List<Card> cardList){List<List<Card>> ret = new ArrayList<>();List<Card> hand0 = new ArrayList<>();List<Card> hand1 = new ArrayList<>();List<Card> hand2 = new ArrayList<>();ret.add(hand0);ret.add(hand1);ret.add(hand2);for (int i = 0; i < 5; i++) {for (int j = 0; j < 3; j++) {ret.get(j).add(cardList.remove(0));}}return ret;}
}
public class Test{public static void main(String[] args) {//买一副52张的牌CardDemo cards = new CardDemo();List<Card> cardList = cards.buyCard();System.out.println(cardList);//洗牌cards.shuffle(cardList);System.out.println(cardList);//3个人,每个人轮流拿五张List<List<Card>> players = cards.play(cardList);for (int i = 0; i < players.size(); i++) {System.out.println("第"+(i+1) +"个人的牌:" + players.get(i));}//剩下的牌:System.out.print("剩下的牌:");System.out.println(cardList);}//结果为://[{1♦}, {1♣}, {1♥}, {1♠}, {2♦}, {2♣}, {2♥}, {2♠}, {3♦}, {3♣}, {3♥}, {3♠}, {4♦}, {4♣}, {4♥}, {4♠}, {5♦}, {5♣}, {5♥}, {5♠}, {6♦}, {6♣}, {6♥}, {6♠}, {7♦}, {7♣}, {7♥}, {7♠}, {8♦}, {8♣}, {8♥}, {8♠}, {9♦}, {9♣}, {9♥}, {9♠}, {10♦}, {10♣}, {10♥}, {10♠}, {11♦}, {11♣}, {11♥}, {11♠}, {12♦}, {12♣}, {12♥}, {12♠}, {13♦}, {13♣}, {13♥}, {13♠}]//[{4♠}, {9♥}, {5♣}, {1♦}, {12♣}, {13♥}, {3♦}, {8♣}, {4♦}, {5♠}, {2♠}, {5♦}, {10♥}, {13♦}, {12♥}, {10♦}, {7♥}, {10♠}, {7♣}, {11♦}, {9♦}, {5♥}, {1♠}, {8♠}, {11♥}, {13♣}, {4♥}, {12♦}, {3♥}, {6♠}, {8♦}, {6♥}, {3♠}, {13♠}, {6♦}, {1♥}, {1♣}, {2♦}, {4♣}, {10♣}, {7♠}, {3♣}, {2♣}, {7♦}, {9♠}, {6♣}, {9♣}, {2♥}, {8♥}, {12♠}, {11♣}, {11♠}]//第1个人的牌:[{4♠}, {1♦}, {3♦}, {5♠}, {10♥}]//第2个人的牌:[{9♥}, {12♣}, {8♣}, {2♠}, {13♦}]//第3个人的牌:[{5♣}, {13♥}, {4♦}, {5♦}, {12♥}]//剩下的牌:[{10♦}, {7♥}, {10♠}, {7♣}, {11♦}, {9♦}, {5♥}, {1♠}, {8♠}, {11♥}, {13♣}, {4♥}, {12♦}, {3♥}, {6♠}, {8♦}, {6♥}, {3♠}, {13♠}, {6♦}, {1♥}, {1♣}, {2♦}, {4♣}, {10♣}, {7♠}, {3♣}, {2♣}, {7♦}, {9♠}, {6♣}, {9♣}, {2♥}, {8♥}, {12♠}, {11♣}, {11♠}]
}

ArrayList的问题及思考

  1. ArrayList底层使用连续的空间,任意位置插入或者删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。
  3. 增容一般是呈1.5倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到150,我们只想继续插入5个数据,后面没有数据插入了,那么就浪费了45个数据空间。

关于ArrayList我们先了解和学习到这,希望这篇文章能帮助到你,谢谢你的阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1541891.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

某招标公告公示搜索引擎爬虫逆向

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 网站&#xff1a;aHR0cHM6Ly9jdGJwc3AuY29tLyMv 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、抓包分析 请求参数和返回数据都进行了加…

AIGC7: 高通骁龙AIPC开发者沙龙过程记录A

图中是一座高耸的宫殿。 就像AI的出现&#xff0c;慢慢初现端倪&#xff0c;头角峥嵘。 背景 一直以来都比较关注AI的发展&#xff0c;有幸再一次参加异常AI的盛会。 从我的角度看。 高通是一家生产芯片的公司&#xff0c;国内的小米&#xff0c;荣耀&#xff0c;Oppo , Vi…

SGFormer:简化并增强Transformer以应对大型图表示的挑战

人工智能咨询培训老师叶梓 转载标明出处 大型图数据的表示学习面临的主要挑战是如何在有限的计算资源下&#xff0c;有效地捕捉节点间的依赖关系并生成有用的节点表示。现有的基于Transformer的方法通常采用多层多头注意力机制&#xff0c;这虽然能够捕获全局信息&#xff0c;…

fasterRCNN模型实现飞机类目标检测

加入会员社群&#xff0c;免费获取本项目数据集和代码&#xff1a;点击进入>> 关于python哥团队 我们是一个深度学习领域的独立工作室。团队成员有&#xff1a;中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等&#xff0c;曾在腾讯、百度、德勤等担任算法工程师…

13.面试算法-字符串常见算法题(二)

1. 字符串反转专题 我们知道反转是链表的一个重要考点&#xff0c;反转同样是字符串的重要问题。常见问题也就是在LeetCode中列举的相关题目&#xff1a; 【1】LeetCode344. 反转字符串&#xff1a;编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符…

【多维动态规划】64. 最小路径和(面试真题+面试官调整后的题目)

64. 最小路径和 难度&#xff1a;中等 力扣地址&#xff1a;https://leetcode.cn/problems/minimum-path-sum/description/ 1. 原题以及解法 1.1 题目 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和…

Redis——redispluspls库list及set类型相关接口使用

文章目录 list 类型相关接口lpush和lrangerpushlpop和rpopblpop和brpopllen set 类型相关接口sadd和smemberssismemberscardspopsinstersinterstore list 类型相关接口 lpush和lrange void lrange_lpush(sw::redis::Redis& redis){std::cout<<"lpush 和 lrang…

Windows控制台中文乱码怎么解决?(nes,一些exe窗口程序)

当我们打开一些Window窗口程序出现中文乱码时&#xff0c;可以像这样设置一下&#xff01; 1、打开 设置-->时间和语言-->语言和区域 2、 管理语言设置 3、更改系统区域设置 4、取消勾选 Beta版&#xff1a;UTF-8 5、效果演示 这下中文不乱码了&#xff01;

多维系统下单点登录之生产实践(2种方案3种实践)

1、基于 Cookie 跨域与分布式 Session 的技术实践 1、XXL-SSO 整体价格 2、实现原理剖析 首次请求 第二次请求 跨域请求 注销流程 3、案例演示 首次登陆跳转至统一认证中心 访问&#xff1a;http://xxlssoclient1.com:8081/ 登陆成功&#xff0c;写入 Cookie&#…

MySQL record 06 part

事务、存储过程 事务&#xff1a; MySQL的同步&#xff0c;同步是指 together done&#xff0c;要么一起前进&#xff0c;要么一起后退的意思。 注意&#xff0c;回滚 rollback 对已经提交 commit 的数据是无效的&#xff0c;也就是说&#xff0c;只能对没有被提交 commit …

【iOS】KVC的学习

【iOS】KVC的学习 文章目录 【iOS】KVC的学习前言KVC定义KVC设值KVC取值KVC使用keyPathKVC处理异常处理nil异常 KVC的一些应用修改动态的设置值实现高阶的消息传递 小结 前言 笔者简单学习了有关与KVC的相关内容&#xff0c;这里写一篇博客简单介绍一下相关内容。 KVC 定义 KV…

saas收银系统源码

1. 线下门店多样化收银 ①门店有社区小店、也会有大店&#xff0c;甚至还会有夫妻店&#xff0c;同时还要有Windows版和安卓版&#xff0c;需满足不同门店的收银需求。 ②支持Windows收银、安卓收银、无人自助收银、聚合码收银等&#xff0c;支持ai智能称重、收银称重一体机等…

『功能项目』QFrameWorkBug拖拽功能【66】

我们打开上一篇65QFrameWork道具栏物品生成的项目&#xff0c; 本章要做的事情是实现物品的拖拽功能 修改脚本&#xff1a;UISlot.cs 实现接口后编写脚本&#xff1a; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace QFramework {publi…

Netty+HTML5+Canvas 网络画画板实时在线画画

采用Html5的canvas做前端画画板&#xff0c;发送数据到后端Netty服务&#xff0c;实时转发笔迹数据&#xff0c;在线实时同步画笔轨迹&#xff0c;单击绿色小方块&#xff0c;保存画板的图片 页面&#xff1a; <!-- index.html --><!DOCTYPE html> <html> …

[Linux]:信号(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 1. 信号的阻塞 1.1 基本概念 信号被操作系统发送给进程之后&#xff0c;进程…

机器学习05-聚类算法(python)SC(轮廓系数)详解

# 导入必要的库 from sklearn.cluster import KMeans # 导入 KMeans 聚类算法 import matplotlib.pyplot as plt # 导入 matplotlib 用于绘图 from sklearn.datasets import make_blobs # 导入 make_blobs 用于生成模拟数据 from sklearn.metrics import silhouette_score …

react:组件通信

组件通信 父组件向子组件通信 function App() {return (<div><div>这是父组件</div><Child name"这是子组件" /></div>); }// 子组件 function Child(props) {return <div>{props.name}</div>; }props说明 props可以传…

浅谈计算机视觉的学习路径1

计算机视觉&#xff08;Computer Vision, CV&#xff09;是人工智能领域的一个重要分支&#xff0c;它的目标是使计算机能够像人类一样理解和处理图像和视频数据。 面向想要从事该方向的大学生&#xff0c;笔者这里给出以下是关于计算机视觉的学习路径建议&#xff1a; 简要了解…

Linux开发工具(git、gdb/cgdb)--详解

目录 一、Linux 开发工具分布式版本控制软件 git1、背景2、使用 git&#xff08;1&#xff09;预备工作——安装 git&#xff1a;&#xff08;2&#xff09;克隆远程仓库到本地&#xff08;3&#xff09;把需要提交的代码拷贝到本地仓库&#xff08;4&#xff09;提交本地仓库文…

一种新的电子邮件攻击方式:AiTM

新的攻击组利用合作伙伴组织之间的信任关系来绕过多重身份验证。 一种新的攻击方式开始出现&#xff0c;它利用合作伙伴组织之间的信任关系绕过多重身份验证。在一个利用不同组织之间关系的攻击中&#xff0c;攻击者成功地对四家或更多组织进行了商业电子邮件欺诈(BEC)攻击&…