文章目录
- ★引言:
- [一. MyArrayList模拟实现](#一. MyArrayList模拟实现)
-
- (一)IList
- (二)MyArrayList
-
- [(1)add(T data)](#(1)add(T data))
- [(2)add(int pos, T data)](#(2)add(int pos, T data))
- (3)IllgalPosException
- [(4)indexOf(Object toFind)](#(4)indexOf(Object toFind))
- [(5)contains(Object toFind)](#(5)contains(Object toFind))
- [(6)get(int pos)](#(6)get(int pos))
- [(7)set(int pos, T value)](#(7)set(int pos, T value))
- [(8)remove(Object toRemove)](#(8)remove(Object toRemove))
- (9)size()
- (10)clear()
- (11)display()
- [(12)remove(int pos)](#(12)remove(int pos))
- (13)EmptyException
- [二. 洗牌算法](#二. 洗牌算法)
-
- (一)Card
- (二)CardArrayList
-
- (1)buyCard()
- [(2)shuffle(MyArrayList<Card> cardList)](#(2)shuffle(MyArrayList<Card> cardList))
- [(3)swap(MyArrayList<Card> cardList,int i,int j)](#(3)swap(MyArrayList<Card> cardList,int i,int j))
- [三. 测试](#三. 测试)
★引言:
该篇博客带大家一起模拟实现一个简易版集合ArrayList,并结合洗牌算法来验证正确性,喜欢的话可以点赞和收藏
一. MyArrayList模拟实现
(一)IList
- 查看ArrayList源码可以发现:ArrayList 继承了抽象类AbstractList ,而AbstractList又实现了List接口
- 这里我们省去AbstractList的过程,直接实现一个IList接口,让ArrayList重写接口当中的方法
java
package list;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 32309
* Date: 2025-07-20
* Time: 15:57
*/
public interface IList<T> {
// 新增元素,默认在数组最后新增
public void add(T data) ;
public void add(int pos, T data) ;
public boolean contains(Object toFind) ;
public int indexOf(Object toFind) ;
// 获取 pos 位置的元素
public Tget(int pos) ;
public void set(int pos, T value) ;
//删除第⼀次出现的关键字key
public boolean remove(Object toRemove) ;
// 获取顺序表⻓度
public int size() ;
// 清空顺序表
public void clear() ;
// 打印顺序表,注意:该⽅法并不是顺序表中的⽅法,为了⽅便看测试结果给出的
public void display();
}
(二)MyArrayList
- 首先我们要创建一个Object数组 ,用来接收所有类型,以及实际使用数量count 和默认容量DEFAULT_CAPACITY
java
public class MyArrayList<T> implements IList<T> {
private Object[] array;
private int count;
private final int DEFAULT_CAPACITY = 10;
}
- 提供两个构造方法,带容量参数initcapacity ,new一个用户想要容量的数组,与不带参数的构造方法,new默认容量的数组
java
/**
* Created with IntelliJ IDEA.
* Description:
* User: 32309
* Date: 2025-07-20
* Time: 15:55
*/
public class MyArrayList<T> implements IList<T> {
private Object[] array;
private int count;
private final int DEFAULT_CAPACITY = 10;
// 默认构造⽅法
public MyArrayList() {
array = new Object[DEFAULT_CAPACITY];
}
// 将顺序表的底层容量设置为initcapacity
public MyArrayList(int initcapacity) {
if (initcapacity > 0) {
array = new Object[initcapacity];
return;
}
array = new Object[DEFAULT_CAPACITY];
}
}
(1)add(T data)
- 这里我们默认采用尾插法,尾插法的时间复杂度为O(1),更为快捷
- 在插入数据之前我们首先要判断的就是,数组是否满了 isFull ,满了就要扩容 grow
java
// 默认尾插
@Override
public void add(T data) {
if (isFull()) {
grow();
}
array[count++] = data;
}
// 扩容
private void grow() {
array = Arrays.copyOf(array,count << 1);
}
// 判满
private boolean isFull() {
return count == array.length;
}
(2)add(int pos, T data)
- 指定下标插入,是add的重载方法
- 插入之前同样要判满与扩容
- 检查下标是否合法 checkPos1
- 下标不合法要抛出异常 IllgalPosException
- 插入时采用从后向前覆盖的方式,给pos位置留出位置
java
// 在 pos 位置新增元素
@Override
public void add(int pos, T data) {
if (isFull()) {
grow();
}
if (checkPos(pos)) {
throw new IllgalPosException("pos下标不合法:" + pos);
}
// 从后往前覆盖
for (int i = count - 1; i >= pos; i--) {
array[i + 1] = array[i];
}
array[pos] = data;
count++;
}
// 检查下标是否合法
private boolean checkPos1(int pos) {
if (pos < 0 || pos > count) {
return true;
}
return false;
}
(3)IllgalPosException
下标不合法异常
java
package list;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 32309
* Date: 2025-07-20
* Time: 16:41
*/
public class IllgalPosException extends RuntimeException{
public IllgalPosException() {
}
public IllgalPosException(String message) {
super(message);
}
}
(4)indexOf(Object toFind)
- 查找对应位置传入值的下标
- 遍历数组查找到返回下标,找不到返回-1,因为数组下标没有-1
- 注意:这里面比较值是否相等要调用 equals方法,不能用 **==**直接比较
java
// 查找某个元素对应的位置
@Override
public int indexOf(Object toFind) {
for (int i = 0; i < count; i++) {
if (array[i].equals(toFind)) {
return i;
}
}
return -1;
}
(5)contains(Object toFind)
- 查找数组中是否有这个值
- 直接调用 indexOf方法,根据返回的下标来判断是否存在
java
// 判定是否包含某个元素
@Override
public boolean contains(Object toFind) {
int index = indexOf(toFind);
return index != -1;
}
(6)get(int pos)
- 获取 pos 位置的元素的方法
- 判断 pos位置是否合法,不合法抛出异常,合法就返回该下标的值
java
// 判断下标知否合法
private boolean checkPos2(int pos) {
if (pos < 0 || pos >= count) {
return true;
}
return false;
}
// 获取 pos 位置的元素
@Override
public T get(int pos) {
if (checkPos2(pos)) {
throw new IllgalPosException("下标位置不合法!!!");
}else {
return (T) array[pos];
}
}
(7)set(int pos, T value)
- 给 pos 位置的元素设为 value的方法
- 先判断 pos 是否合法,再设置值
java
// 给 pos 位置的元素设为 value
@Override
public void set(int pos, T value) {
if (checkPos2(pos)) {
throw new IllgalPosException("下标位置不合法!!!");
}
array[pos] = value;
}
(8)remove(Object toRemove)
- 删除第⼀次出现的关键字key的方法
- 先判断数组有没有这个值
- 采用从前向后覆盖方式,同时注意将末尾位置置为null
java
// 删除第⼀次出现的关键字key
@Override
public boolean remove(Object toRemove) {
int index = indexOf(toRemove);
if (index == -1) {
System.out.println("没有你要删除的值!!!");
return false;
}
for (int i = index; i < count - 1; i++) {
array[i] = array[i + 1];
}
array[count--] = null;
return true;
}
(9)size()
返回顺序表长度
java
// 获取顺序表⻓度
@Override
public int size() {
return count;
}
(10)clear()
清空顺序表 ,注意:需要将数组中所有元素都置为null
java
// 清空顺序表
@Override
public void clear() {
for (int i = 0; i < count; i++) {
array[i] = null;
}
count = 0;
}
(11)display()
打印顺序表
java
// 打印顺序表
@Override
public void display() {
for (int i = 0; i < count; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
(12)remove(int pos)
- remove 的重载方法,该方法可以返回删除位置的元素
- 需要验证数组中是否是空的,以及pos位置是否合法
java
public T remove(int pos) {
if (isEmpty()) {
throw new EmptyException("空数组异常!!!");
}
if (checkPos2(pos)) {
throw new IllgalPosException("pos 下标非法!!!");
}
T t = get(pos);
remove(t);
return t;
}
(13)EmptyException
空数组异常
java
package list;
public class EmptyException extends RuntimeException {
public EmptyException() {
}
public EmptyException(String message) {
super(message);
}
}
二. 洗牌算法
(一)Card
一张扑克牌上的属性有花色 suit 与 数字 rank
java
package card;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 32309
* Date: 2025-07-20
* Time: 9:55
*/
public class Card {
private String suit;
private int rank;
public Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
public String getSuit() {
return suit;
}
public void setSuit(String suit) {
this.suit = suit;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
@Override
public String toString() {
return "{" + suit + rank + "}";
}
}
(二)CardArrayList
- 所有扑克牌放到该集合中
- 使用我们刚写好的MyArrayList
- 分为三部分:买牌 buyCard ,洗牌 shuffle ,交换 swap
- 设置号四种花色
(1)buyCard()
54张扑克牌,1~13,每张花色不同,外加小王与大王
java
private static final String[] suits = {"♥","♠","♦","♣"};
public static MyArrayList<Card> buyCard() {
MyArrayList<Card> cards = new MyArrayList<>(54);
for (int i = 1; i <= 13; i++) {
int rank = i;
for (int j = 0; j < 4; j++) {
String suit = suits[j];
Card card = new Card(suit,rank);
cards.add(card);
}
}
Card card1 = new Card("大王🃏",999);
cards.add(card1);
Card card2= new Card("小王🃏",888);
cards.add(card2);
return cards;
}
(2)shuffle(MyArrayList cardList)
通过设置随机数,从末尾开始来交换,来达到洗牌目的
java
public static void shuffle(MyArrayList<Card> cardList) {
Random random = new Random();
for (int i = cardList.size() - 1; i > 0; i--) {
int index = random.nextInt(i);
swap(cardList,i,index);
}
}
(3)swap(MyArrayList cardList,int i,int j)
作为洗牌的辅助方法
java
private static void swap(MyArrayList<Card> cardList,int i,int j) {
Card card = cardList.get(i);
cardList.set(i, cardList.get(j));
cardList.set(j,card);
}
三. 测试
- 分别调用买牌 buyCard ,洗牌 shuffle,这两二方法来打印牌的信息来测试方法是否正确
- 三个人轮流揭牌,把所有牌全部揭完
java
package list;
import card.Card;
import card.CardArrayList;
public class Test {
public static void main(String[] args) {
MyArrayList<Card> cards = CardArrayList.buyCard();
System.out.print("买牌后:");
cards.display();
System.out.print("洗牌后:");
CardArrayList.shuffle(cards);
cards.display();
// 三个人轮流抓五张牌
MyArrayList<MyArrayList<Card>> hands = new MyArrayList<>();
hands.add(new MyArrayList<>());
hands.add(new MyArrayList<>());
hands.add(new MyArrayList<>());
// 抓牌
for (int i = 0; i < 18; i++) {
for (int j = 0; j < 3; j++) {
// 获取第几个人
hands.get(j).add(cards.remove(0));
}
}
System.out.print("A 的牌:");
hands.get(0).display();
System.out.print("B 的牌:");
hands.get(1).display();
System.out.print("C 的牌:");
hands.get(2).display();
System.out.print("剩余的牌:");
cards.display();
}
}
