ArrayList 与 顺序表 (附洗牌算法)!

news2025/1/20 16:29:19

 

曾经我也是一枚学霸,直到有一天想去学渣的世界看看,结果就找不到回去的路了。

目录

1. 线性表

2.顺序表 

2.1 接口的实现

3. ArrayList简介

4. ArrayList使用

4.1 ArrayList的构造 

 4.2 ArrayList常见操作

4.3 ArrayList的遍历

4.4 ArrayList的扩容机制 

5. ArrayList的具体使用

5.1 简单的洗牌算法 

6. ArrayList的问题及思考


1. 线性表

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

 

2.顺序表 

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


2.1 接口的实现

public class SeqList {
    private int[] array;
    private int size;
    // 默认构造方法
    SeqList(){ }
    // 将顺序表的底层容量设置为initcapacity
    SeqList(int initcapacity){ }
    // 新增元素,默认在数组最后新增
    public void add(int data) { }
    // 在 pos 位置新增元素
    public void add(int pos, int data) { }
    // 判定是否包含某个元素
    public boolean contains(int toFind) { return true; }
    // 查找某个元素对应的位置
    public int indexOf(int toFind) { return -1; }
    // 获取 pos 位置的元素
    public int get(int pos) { return -1; }
    // 给 pos 位置的元素设为 value
    public void set(int pos, int value) { }
    //删除第一次出现的关键字key
    public void remove(int toRemove) { }
    // 获取顺序表长度
    public int size() { return 0; }
    // 清空顺序表
    public void clear() { }

    // 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
    public void display() { }
}

 

3. 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底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

 

4. ArrayList使用

4.1 ArrayList的构造 

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("111");
    list4.add(100);
}

 4.2 ArrayList常见操作

ArrayList虽然提供的方法比较多,但是常用方法如下所示,需要用到其他方法时,自行查看ArrayList的帮助文档即可。
 


public static void main(String[] args) {
     List<String> list = new ArrayList<>();
     list.add("JavaSE");
     list.add("JavaWeb");
     list.add("JavaEE");
     list.add("JVM");
     list.add("测试课程");
     System.out.println(list);

     //获取list中有效元素个数
     System.out.println(list.size());

     //获取和设置index位置上的元素,注意index必须介于[0,size)区间
     System.out.println(list.get(1));
     list.set(1,"JavaWEB");
     System.out.println(list.get(1));

     //在list的index位置插入指定元素,index及后续的元素统一往后搬移一个位置
     list.add(1,"java数据结构");
     System.out.println(list);
     //删除指定元素,找到了就删除,该元素之后的元素统一往前搬移一个位置
     list.remove("JVM");
     System.out.println(list);
     //删除list中index位置上的元素,注意index不要超过list中有效元素个数,否则会抛出下标越界异常
     list.remove(list.size() - 1);
     System.out.println(list);
     //检测list中是否包含指定元素,包含返回true,否则返回false
     if(list.contains("测试课程")) {
         list.add("测试课程");
     }

     //查找指定元素第一次出现的位置:indexOf从前往后找,lastIndexOf从后往前找
     list.add("JavaSE");
     System.out.println(list);
     System.out.println(list.indexOf("javaSE"));  //区分大小写,找不到返回-1
     System.out.println(list.lastIndexOf("JavaSE"));

    //使用list中[0,4)之间的元素构成一个新的SubList返回,但是和ArrayList共用一个elementData数组
     List<String> ret = list.subList(0,4);
     System.out.println(ret);

     list.clear();
     System.out.println(list.size());

}

4.3 ArrayList的遍历

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

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    // 使用下标+for遍历
    for (int i = 0; i < list.size(); i++) {
        System.out.print(list.get(i) + " ");
    } 
    System.out.println();
    // 借助foreach遍历
    for (Integer integer : list) {
        System.out.print(integer + " ");
    }
    System.out.println();
    Iterator<Integer> it = list.listIterator();
    while(it.hasNext()){
        System.out.print(it.next() + " ");
    } 
    System.out.println();
}

注意:
        1. ArrayList最长使用的遍历方式是:for循环+下标 以及 foreach
        2. 迭代器是设计模式的一种,后序容器接触多了再给大家铺垫

 

4.4 ArrayList的扩容机制 

下面代码有缺陷吗?为什么? 

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        list.add(i);
    }
}

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 code
    if (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进行扩容

 

5. ArrayList的具体使用

5.1 简单的洗牌算法 

public class Card {
    public int rank; // 牌面值
    public String suit; // 花色
    @Override
    public String toString() {
        return String.format("[%s %d]", suit, rank);
    }
} 



import java.util.List;
import java.util.ArrayList;
import java.util.Random;

public class CardDemo {
    public static final String[] SUITS = {"♠", "♥", "♣", "♦"};
    // 买一副牌
    private static List<Card> buyDeck() {
        List<Card> deck = new ArrayList<>(52);
        for (int i = 0; i < 4; i++) {
            for (int j = 1; j <= 13; j++) {
                String suit = SUITS[i];
                int rank = j;
                Card card = new Card();
                card.rank = rank;
                card.suit = suit;
                deck.add(card);
            }
        } 

        return deck;
    }

    private static void swap(List<Card> deck, int i, int j) {
        Card t = deck.get(i);
        deck.set(i, deck.get(j));
        deck.set(j, t);
    }

    private static void shuffle(List<Card> deck) {
        Random random = new Random(20190905);
        for (int i = deck.size() - 1; i > 0; i--) {
            int r = random.nextInt(i);
            swap(deck, i, r);
        }
    }

    public static void main(String[] args) {
        List<Card> deck = buyDeck();
        System.out.println("刚买回来的牌:");
        System.out.println(deck);
        shuffle(deck);
        System.out.println("洗过的牌:");
        System.out.println(deck);
        // 三个人,每个人轮流抓 5 张牌
        List<List<Card>> hands = new ArrayList<>();
        hands.add(new ArrayList<>());
        hands.add(new ArrayList<>());
        hands.add(new ArrayList<>());

        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 3; j++) {
                hands.get(j).add(deck.remove(0));
            }
        } 

        System.out.println("剩余的牌:");
        System.out.println(deck);
        System.out.println("A 手中的牌:");
        System.out.println(hands.get(0));

        System.out.println("B 手中的牌:");
        System.out.println(hands.get(1));
        System.out.println("C 手中的牌:");
        System.out.println(hands.get(2));
    }
}
刚买回来的牌:
[[♠ 1], [♠ 2], [♠ 3], [♠ 4], [♠ 5], [♠ 6], [♠ 7], [♠ 8], [♠ 9], [♠ 10], [♠ 11], [♠ 12], [♠ 13], [♥ 1], [♥ 2], [♥ 3], [♥ 4], [♥ 5], [♥ 6], [♥ 7], [♥ 8], [♥ 9], [♥ 10], [♥ 11], [♥ 12], [♥ 13], [♣ 1], [♣ 2], [♣ 3], [♣ 4], [♣ 5], [♣ 6], [♣ 7], [♣ 8], [♣ 9], [♣ 10], [♣ 11], [♣ 12], [♣ 13], [♦ 1], [♦ 2], [♦ 3], [♦ 4], [♦ 5], [♦ 6], [♦ 7], [♦ 8], [♦ 9], [♦ 10], [♦ 11], [♦ 12], [♦ 13]]
洗过的牌:
[[♥ 11], [♥ 6], [♣ 13], [♣ 10], [♥ 13], [♠ 2], [♦ 1], [♥ 9], [♥ 12], [♦ 5], [♥ 8], [♠ 6], [♠ 3], [♥ 5], [♥ 1], [♦ 6], [♦ 13], [♣ 12], [♦ 12], [♣ 5], [♠ 4], [♣ 3], [♥ 7], [♦ 3], [♣ 2], [♠ 1], [♦ 2], [♥ 4], [♦ 8], [♠ 10], [♦ 11], [♥ 10], [♦ 7], [♣ 9], [♦ 4], [♣ 8], [♣ 7], [♠ 8], [♦ 9], [♠ 12], [♠ 11], [♣ 11], [♦ 10], [♠ 5], [♠ 13], [♠ 9], [♠ 7], [♣ 6], [♣ 4], [♥ 2], [♣ 1], [♥ 3]]
剩余的牌:
[[♦ 6], [♦ 13], [♣ 12], [♦ 12], [♣ 5], [♠ 4], [♣ 3], [♥ 7], [♦ 3], [♣ 2], [♠ 1], [♦ 2], [♥ 4], [♦ 8], [♠ 10], [♦ 11], [♥ 10], [♦ 7], [♣ 9], [♦ 4], [♣ 8], [♣ 7], [♠ 8], [♦ 9], [♠ 12], [♠ 11], [♣ 11], [♦ 10], [♠ 5], [♠ 13], [♠ 9], [♠ 7], [♣ 6], [♣ 4], [♥ 2], [♣ 1], [♥ 3]]
A 手中的牌:
[[♥ 11], [♣ 10], [♦ 1], [♦ 5], [♠ 3]]
B 手中的牌:
[[♥ 6], [♥ 13], [♥ 9], [♥ 8], [♥ 5]]
C 手中的牌:
[[♣ 13], [♠ 2], [♥ 12], [♠ 6], [♥ 1]]

 

6. ArrayList的问题及思考

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

所以ArrayList存在缺陷,而这缺陷就需要靠LinkedList解决了。

 

( 注:图片来自网络,如有侵权,请联系删除 )  

希望对大家有所帮助,感谢观看!!!

 

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

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

相关文章

永倍达电商模式分析:创新商业模式引领新时代购物潮

在2019年底&#xff0c;全球新冠疫情席卷&#xff0c;导致大量实体经济倒闭&#xff0c;人们纷纷居家躲避&#xff0c;经济陷入下行&#xff0c;企业家面临倒闭威胁。永倍达成立于2020年&#xff0c;是陕西永倍达电子商务有限公司的品牌&#xff0c;而其母公司实际上是天津铸源…

Stable Diffusion 系列教程 - 1 基础准备(针对新手)

使用SD有两种方式&#xff1a; 本地&#xff1a; 显卡要求&#xff1a;硬件环境推荐NVIDIA的具有8G显存的独立显卡&#xff0c;这个显存勉勉强强能摸到门槛。再往下的4G可能面临各种炸显存、炼丹失败、无法生成图片等各种问题。对于8G显存&#xff0c;1.0模型就不行&#xff0…

前端入门(四)Ajax、Promise异步、Axios通信、vue-router路由、组件库

文章目录 AjaxAjax特点 Promise 异步编程&#xff08;缺&#xff09;Promise基本使用状态 - PromiseState结果 - PromiseResult AxiosVue中使用AxiosAxios请求方式getpostput和patchdelete并发请求 Vue路由 - vue-router单页面Web应用&#xff08;single page web application&…

C++ string类(2)—成员访问、插入、删除、替换、查找和交换操作

目录 一、成员访问 1、[ ]&at 2、front( )&back( ) 二、插入元素 三、删除元素 四、替换元素 五、查找元素 1、查找第一次出现位置 2 、在指定范围内查找 六、交换字符串 七、c_str 八、rfind&substr 一、成员访问 1、[ ]&at 虽然二者功能一样&…

LeetCode | 101. 对称二叉树

LeetCode | 101. 对称二叉树 OJ链接 在本函数里不好进行判断&#xff0c;我们另外定义一个函数来如果两个都相等为空&#xff0c;就返回true一个为空&#xff0c;一个不为空都不为空,就比较值然后递归1的左&#xff0c;2的右&#xff0c;1的右&#xff0c;2的左 bool _isSymm…

javaee实验:MVC 框架技术应用——URL 映射及方法参数的使用

目录 urlmvc框架mvc框架的设计mvc流程 实验目的实验内容实验过程创建项目创建项目结构编写代码简单测试一下 url 和 Hypertext 以及 HTTP 一样&#xff0c;URL 是 Web 中的一个核心概念。它是浏览器用来检索 web 上公布的任何资源的机制 URL 代表着是统一资源定位符&#xff…

《洛谷深入浅出进阶篇》模意义下的乘法逆元+洛谷P3811

什么是乘法逆元&#xff1f; 算数意义上的乘法逆元指的是倒数&#xff0c;即&#xff1a;a*&#xff08;1/a&#xff09;1 所以 1/a 是 a在算数意义下的乘法逆元&#xff0c;或者可以说二者互为逆元。 这有什么用呢&#xff1f; 除以a就等于乘上a的乘法逆元&#xff0c;乘以…

keil5 --工程创建

一&#xff0c;文件夹介绍 首先去官网过去其他地方获取到官方提供的标准库文件 下面这个我是在官网进行下载的 我们在打开keil的时候会弹出一个在线下载的框&#xff08;这个框这里先不做说明&#xff0c;后面在继续讲解&#xff09;&#xff0c;我们不使用这个在线下载功能&a…

LangChain 19 Agents Reason+Action自定义agent处理OpenAI的计算缺陷

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

配置SAP用户密码策略(不用重启服务器的方法)

最近公司审计&#xff0c;给我们提出了要优化密码策略的建议&#xff0c;原因是我们的密码策略太简单了。我稍稍研究了一下。之前是通过RZ10来配置&#xff0c;但是这种方法需要重启服务器&#xff0c;这就比较麻烦。其实有一种方法是通过配置密码策略&#xff0c;不要要重启的…

包装材料ERP是什么?包装材料ERP有什么用

市面上的包装材料种类多种多样&#xff0c;而这些差异化的包装材料对应的产成品规格、型号、质量、销售策略和生产工艺等方面存在诸多差异。 另外&#xff0c;通常包装材料企业的营销渠道比较广泛&#xff0c;不同的销售平台有多样化的业务流程和管理方式&#xff0c;相同的商…

8-2比较三个字符串大小(指针)

#include<stdio.h> #include<string.h>int main(){char str1[20], str2[20], str3[59];//相当于 int a,b,c;printf("please input three str:\n");gets(str1);//sacnf("%s",str1);这里str1是数组名&#xff0c;表示数组空间的起始地址&#xf…

qt 动态生成柱状图

cpp文件 #include "mform.h" #include "ui_mform.h" #include <QBarSeries> #include <QBarSet> #include <QtCharts> #include <QPushButton> #include <QtCharts/QChartView> #include <QtCharts/QPieSeries> #inc…

图解系列--功能追加协议,构建Web内容

功能追加协议 1.消除 HTTP 瓶颈的 SPDY 1.1.HTTP 的瓶颈 使用 HTTP 协议探知服务器上是否有内容更新&#xff0c;就必须频繁地从客户端到服务器端进行确认。如果服务器上没有内容更新&#xff0c;那么就会产生徒劳的通信。 若想在现有 Web 实现所需的功能&#xff0c;以下这些…

LLM | 一文搞懂Langchain的概念,相关组件,以及大模型微调~

本文主要介绍了 Langchain的概念&#xff0c;相关组件&#xff0c;以及大模型微调代码实现~ 1.什么是LangChain&#xff1f; LangChain是一个平台&#xff0c;提供工具和API&#xff0c;用于构建由语言模型&#xff08;LLM&#xff09;驱动的应用程序。它简化了 LLM 与项目的集…

自动驾驶学习笔记(十三)——感知基础

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 传感器 测距原理 坐标系 标定 同…

一个用c#瞎写的sftp工具

0.下载地址 https://wwus.lanzouj.com/iOZUv1gkgpze 密码:123456 1.能进行单个和批量下载, 没有弄上传 2.速度奇差,可能是某些地方没弄好.有一定的进度显示,但是不太准. 3.很多地方没弄好,有能力的自己弄一下 4.在app.config文件配置sftp

大小堆的实现(C语言)

目录 前言 一种完全二叉树&#xff1a;堆 堆的概念 堆的性质 建堆的时间复杂度 建堆的空间复杂度&#xff1a; 小堆的实现 必要补充 堆的初始化 堆的销毁 向上调整算法 堆的插入 向下调整算法 堆的删除 获取堆顶元素 获取堆中元素个数 堆的判空 最终代码 He…

SpringSecurity 三更草堂 学习笔记

SpringSecurity从入门到精通 0. 简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的…

数据结构奇妙旅程之顺序表和链表

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …