2. 基础数据结构-数组

news2024/11/28 8:51:13

2. 基础数据结构-数组

2.1 概念

        数组是一种数据结构,它是一个由相同类型元素组成的有序集合。在编程中,数组的定义是创建一个具有特定大小和类型的存储区域来存放多个值。数组可以是一维、二维或多维的。每个元素至少有一个索引或键来标识。

 2.2 数组特点

(1)固定大小:数组的大小在创建时就被确定下来,并且不能在后续操作中更改。这意味着一旦创建了数组,就不能添加或删除元素(除非使用新的数组来替换旧的数组)。
(2)相同数据类型:数组中的所有元素必须是同一数据类型的。例如,一个整数数组只能存储整数,而不能混杂着字符串或其他类型的数据。
(3)连续内存空间:数组中的元素在内存中是连续存放的。
(4)索引访问:数组元素是通过索引来访问的,索引通常从0开始。因此,第一个元素的索引是0,第二个元素的索引是1,以此类推。
(5)高效的随机访问:由于数组元素在内存中是连续存放的,所以可以快速地通过索引访问到任何一个元素,时间复杂度为O(1)。
(6)有序性:虽然数组本身并没有规定其元素必须按照特定顺序排列,但通常在编程中会把数组看作是有序的数据结构,因为它的元素是按索引顺序存储的。
(7)可变性:数组中元素值是可改变的,只要该元素的数据类型与数组元素类型兼容即可。
(8)一维、二维或多维:数组可以是一维的(即线性的),也可以是二维或多维的。多维数组通常用于表示表格数据或其他复杂的网格结构。

这些特性使得数组在许多场景下非常有用,尤其是在需要对大量同类型数据进行高效访问和处理的时候。

2.3 数组特点(扩展)

2.3.1 数组的存储

        因为数组中的元素在内存中是连续存放的。这意味着可以通过计算每个元素相对于数组开始位置的偏移量来访问它们,从而提高访问速度。 数组起始地址为BaseAddress,可以使用公式BaseAddress+ i *size,计算出索引 i 元素的地址,i 即是索引,java和C等语言中,都是从0开始。size是每个元素占用的字节,例如int占用4字节,double占用8字节。

        因此,数组的随机访问和数据规模无关,时间复杂度为O(1)。

2.3.2 空间占用

JAVA的数组结构包含:markword(8字节),class指针(4字节),数组大小(4字节)。

(1)数组本身是一个对象。每个Java对象都有一个对象头(Object Header),其中包含了类指针和Mark Word等信息。。Mark Word是HotSpot虚拟机设计的一种数据结构,用于存储对象的运行时元数据。

(2)Mark Word的作用主要包括:

        (2.1)对象的锁状态:Mark Word中的部分内容会根据对象是否被锁定而改变。例如,如果数组正在被synchronized同步块或方法保护,那么这部分内容将包含有关锁的信息,如线程ID、锁状态等。
        (2.2)垃圾收集信息:Mark Word还存储了与垃圾收集相关的信息,如对象的分代年龄和类型指针等。这对于垃圾收集器跟踪和回收对象非常关键。
其他标志位:除此之外,Mark Word可能还包括一些其他的标志位,如偏向锁标志、轻量级锁标志等。
        (2.3)需要注意的是,由于Mark Word是为整个对象服务的,所以它并不直接针对数组元素。数组元素的数据是存储在对象头之后的实例数据区域中。Mark Word主要是为了支持JVM进行各种操作,比如内存管理和并发控制等。

(3)类指针:类指针指向的是该对象所属的类元数据(Class Metadata)。这个指针对于运行时的动态绑定、方法调用以及反射操作非常重要。它存储了关于对象类型的所有信息,包括类名、父类、接口、字段、方法、常量池等。在64位JVM上,类指针通常占用8字节。而在32位JVM上,类指针占用4字节。

(4)数组大小:数组大小为4字节,因此决定了数组最大容量为2^32,数组元素+字节对齐(JAVA中所有对象的大小都是8字节的整数倍,不足的要用对齐字节补足)

例如:

int [] arr={1,2,3,4,5}

该数组包含内容包括:

单位(字节)

markword(8)
class指针(4)数组大小(4)
1(4)2(4)
3(4)4(4)
5(4)字节对齐(4)

大小为:8+4+4+4*4+4+4=40字节

2.3.3 动态数组

        因为数组的大小是固定的,所以数组中的元素并不能随意地添加和删除。这种数组称之为静态数组。

        JAVA中的ArrayList是已经创建好的动态数组。

插入或者删除性能时间复杂度:

        头部位置:O(n)

        中间位置:O(n)

        尾部位置:O(1) 均摊来说

package org.alogorithm;


import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.IntStream;

public class Main02 {

    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.addLast(1);
        myArray.addLast(2);
        myArray.addLast(3);
        myArray.addLast(4);
        myArray.addLast(5);
        myArray.addLast(7);
        myArray.addLast(8);
        myArray.addLast(9);
        myArray.addLast(10);
        myArray.addLast(11);
        /*for (int i = 0; i < myArray.size; i++) {
            System.out.println(myArray.array[i]);
        }*/
        myArray.foreach((e) -> {
            //具体操作由调用方界定
            System.out.println(e + "Consumer");
        });
        myArray.add(2, 6);

        for (Integer integer : myArray) {
            System.out.println(integer + "Iterable");
        }

        System.out.println(myArray.remove(4)+"元素被删除");
        myArray.stream().forEach(e -> {
            System.out.println(e + "stream");
        });

    }

    static class MyArray implements Iterable<Integer> {
        private int size = 0;//逻辑大小
        private int capacity = 8;//容量
        private int[] array = {};

        public void addLast(int value) {
            /*array[size] = value;
            size++;*/
            add(size, value);
        }

        public void add(int index, int value) {
            //容量不够扩容
            checkAndGrow();
            /*
             * param1 :要copy的数组
             * param1 :copy的起始位置
             * param1 :要存放数组
             * param1 :要copy的个数
             * 这个方法表示要将array从index开始copy到index+1的位置,
             * copy的个数是数组的大小-index(index向后的元素)
             * */
            if (index >= 0 && index <= size) {
                System.arraycopy(array, index, array, index + 1, size - index);
            }
            //合并add方法
            array[index] = value;
            size++;
        }

        private void checkAndGrow() {
            if (size==0){
                array=new int[capacity];
            }else if (size == capacity) {
                capacity+=capacity>>1;
                int[] newArray = new int[capacity];
                //赋值数组
                System.arraycopy(array,0,newArray,0,size);
                array=newArray;
            }
        }

        //查询元素
        public int get(int index) {
            return array[index];
        }

        //遍历数组 1
        //查询元素
        public void foreach(Consumer<Integer> consumer) {
            for (int i = 0; i < size; i++) {
                //System.out.println(array[i]);
                //提供array[i],不需要返回值
                consumer.accept(array[i]);
            }
        }


        //遍历数组2 迭代器模式
        @Override
        public Iterator<Integer> iterator() {
            //匿名内部类
            return new Iterator<Integer>() {
                int i = 0;

                @Override
                public boolean hasNext() {
                    //有没有下一个元素
                    return i < size;
                }

                @Override
                public Integer next() {
                    //返回当前元素,并将指针移向下一个元素
                    return array[i++];
                }
            };
        }

        //遍历数组3 数据流
        public IntStream stream() {
            return IntStream.of(Arrays.copyOfRange(array, 0, size));
        }

        public int remove(int index) {
            int removeed = array[index];
            System.arraycopy(array, index + 1, array, index, size - index - 1);
            size--;
            return removeed;
        }
    }
}

2.4 二维数组

(1)二维数组占32个字节,其中array[0],array[1],array[2]元素分别保存了指向三个一维数组的引用。

(2)三个一维数组各占40个字节。

(3)他们在内存布局上是连续的。

package org.alogorithm.array;
public class Main03 {
    public static void main(String[] args) {
        int rows = 1_000_000;
        int columns = 14;
        int[][] arr = new int[rows][columns];
        long ijStar = System.currentTimeMillis();
        ij(arr, rows, columns);
        long ijEnd = System.currentTimeMillis();
        System.out.println("ij执行时间为:"+(ijEnd-ijStar));//ij执行时间为:10


        long jiStar = System.currentTimeMillis();
        ji(arr, rows, columns);
        long jiEnd = System.currentTimeMillis();
        System.out.println("ji执行时间为:"+(jiEnd-jiStar));//ji执行时间为:47
    }

    public static void ji(int[][] arr, int rows, int columns) {
        int sum = 0;
        for (int j = 0; j < columns; j++) {
            for (int i = 0; i < rows; i++) {
                sum += arr[i][j];
            }
        }
        System.out.println(sum+"ji");

    }
    public static void ij(int[][] arr, int rows, int columns) {
        int sum = 0;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                sum += arr[i][j];
            }
        }
        System.out.println(sum+"ij");
    }
}

        在计算机中,内存是分块存储的。每个内存块称为一个页(page)。当程序访问数组时,CPU会从内存中加载包含该元素所在的整个页面到高速缓存(cache)中。

在这个例子中,ij和ji两个方法的主要区别在于它们遍历数组的方式:

        ij按行遍历数组:它首先遍历所有的行,然后在同一行内遍历列。
        ji按列遍历数组:它先遍历所有的列,然后在同一列内遍历行。
由于现代计算机体系结构的设计特点,ij比ji更快的原因有以下几点:

        (1)缓存局部性(Cache Locality):按行遍历数组时,相邻的元素在物理内存中彼此靠近,这使得CPU可以高效地利用缓存来加速数据访问。这是因为通常情况下,一次内存读取操作将加载一整块数据(通常是64字节),如果这一块数据中的多个元素被连续使用,那么这些元素很可能都在同一块缓存中,从而减少了对主内存的访问次数。
        (2)预取(Prefetching):一些现代处理器具有预取机制,可以预测接下来可能需要的数据并提前将其加载到缓存中。按照行顺序遍历数组时,这种机制更有可能发挥作用,因为相邻的元素更可能在未来被访问。
综上所述,ij的遍历方式更适合计算机硬件的工作原理,因此执行速度更快。

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

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

相关文章

C++——C++11(1)

时至今日&#xff0c;C标准已经到了C23&#xff0c;但是你要说哪一次提出的标准最经 典&#xff0c;那C11一定会被人提及&#xff0c;C11带来了数量可观的变化&#xff0c;其中包 含了约140个新特性&#xff0c;以及对C03标准中约600个缺陷的修正&#xff0c;这使得 C11更像是从…

no module named ‘xxx‘

目录结构如下 我想在GCNmodel的model里引入layers的GraphConvolution&#xff1a;from GCNmodel.layers import GraphConvolution&#xff0c;但这样却报错no module named GCNmodel&#xff0c;而且用from layers import GraphConvolution也不行。然后用sys.path.appen(xxx)…

echarts地图的常见用法:基本使用、区域颜色分级、水波动画、区域轮播、给地图添加背景图片和图标、3d地图、飞线图

前言 最近几天用echarts做中国地图&#xff0c;就把以前写的demo&#xff1a;在vue中实现中国地图 拿来用&#xff0c;结果到项目里直接报错了&#xff0c;后来发现是因为版本的问题&#xff0c;没办法只能从头进行踩坑了。以下内容基于vue3 和 echarts 5.32 基本使用 获取地…

广受好评的开源基础大模型最全梳理,你最钟意哪一个?

2023 年即将过去。一年以来&#xff0c;各式各样的大模型争相发布。当 OpenAI 和谷歌等科技巨头正在角逐时&#xff0c;另一方「势力」悄然崛起 —— 开源。 开源模型受到的质疑一向不少。它们是否能像专有模型一样优秀&#xff1f;是否能够媲美专有模型的性能&#xff1f; 迄…

go原生http开发简易blog(一)项目简介与搭建

文章目录 一、项目简介二、项目搭建前置知识三、首页- - -前端文件与后端结构体定义四、配置文件加载五、构造假数据- - -显示首页内容 代码地址&#xff1a;https://gitee.com/lymgoforIT/goblog 一、项目简介 使用Go原生http开发一个简易的博客系统&#xff0c;包含一下功能…

CCF-CSP真题《202309-5 阻击》思路+ c++满分题解

想查看其他题的真题及题解的同学可以前往查看&#xff1a;CCF-CSP真题附题解大全 试题编号&#xff1a;202309-5试题名称&#xff1a;阻击时间限制&#xff1a;2.0s内存限制&#xff1a;512.0MB问题描述&#xff1a; 问题描述 上回提到&#xff0c;西西艾弗岛下方有一个庞大的遗…

PyTorch自动梯度计算(注意点)

if params.grad is not None: params.grad.zero_() 我们实际的运算往往会涉及到若干个requires-grad为true的张量进行运算&#xff0c;在这种情况下&#xff0c;Pytorch会计算整个计算图上的损失的导数&#xff0c;并把这些结果累加到grad属性中。多次调用backward()会导致梯度…

chrome升级后,调试vue在控制台输出总是显示cjs.js

当前chrome版本120.0.6099.72 在vue中使用console.log输出时&#xff0c;总是显示cjs.js多少多少行&#xff0c;不能显示源文件名及行数 【解决方案】 打开控制台的设置 左侧找到“Ignore List”&#xff0c;取消勾选"enable Lgnore Listing"&#xff0c;并重启chr…

基于C/C++的非系统库自定义读写ini配置

INI文件由节、键、值组成。 节 [section] 参数 &#xff08;键值&#xff09; namevalue 这里将常用的操作方式封装成了一个dll供外部使用 // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LIBCFG_EXPORTS // 符号…

紫光FPGA DDR3 IP使用和注意事项(axi4协议)

紫光DDR3 IP使用 对于紫光ddr3 IP核的使用需要注意事情。 阅读ddr ip手册&#xff1a; 1、注意&#xff1a;对于写地址通道&#xff0c;axi_awvalid要一直拉高&#xff0c;axi_awready才会拉高。使用的芯片型号时PG2L100H-6FBG676&#xff0c;不同的型号IP核接口和axi的握手协…

wireshark下载安装

下载 Wireshark Download 等待下载完成 安装 双击 下面的一定垚勾选上 下图的也一定要勾选上 修改为不重启&#xff0c;不需要重启也是正常的

嵌入式中的门电路详讲

NOT门电路 NOT(非门)是数字逻辑电路中的一种基本逻辑门,也称为反相器。它执行的是逻辑非操作,即将输入信号取反。NOT门具有一个输入和一个输出。 A输入,B输出,以下是真值表: A B 0 1 1 0 AND门电路 AND(与门)是数字逻辑电路中的一种基本逻辑门,用于执行逻辑与操作。…

从 0 开始实现一个 SpringBoot + Vue 项目

从 0 开始实现一个 SpringBoot Vue 项目 从 0 开始实现一个 SpringBoot Vue 项目软件和工具创建 SpringBoot 后端项目创建 MySQL 数据库配置文件实现增删改查接口Model 层mapper 层service 层controller 层测试 从 0 开始实现一个 SpringBoot Vue 项目 软件和工具 后端开发…

jmeter如何循环运行到csv文件最后一行后停止

1、首先在线程组中设置’循环次数‘–勾选永远 2、csv数据文件设置中设置&#xff1a; 遇到文件结束符再次循环?——改为&#xff1a;False 遇到文件结束符停止线程?——改为&#xff1a;True 3、再次运行就会根据文档的行数运行数据 &#xff08;如果需要在循环控制器中&…

[python]用python获取EXCEL文件内容并保存到DBC

目录 关键词平台说明背景所需库实现过程方法1.1.安装相关库2.代码实现 关键词 python、excel、DBC、openpyxl 平台说明 项目Valuepython版本3.6 背景 在搭建自动化测试平台的时候经常会提取DBC文件中的信息并保存为excel或者其他文件格式&#xff0c;用于自动化测试。本文…

STL容器之string(上)

目录 什么是STL string类 string类常见接口 string类的常见构造函数 string类对象的容器操作 string类对象的访问及遍历操作 string类对象的修改操作 拓展 从本期开始&#xff0c;我们将正式学习C中的STL&#xff0c;美国的麦克阿瑟将军说过&#xff1a;“C不能没有STL就…

轻量封装WebGPU渲染系统示例<52>- Json数据描述材质、纹理等3D渲染场景信息

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/material/src/voxgpu/sample/DataDrivenScene3.ts 当前示例运行效果: ​​​​​​​ Json数据: {"renderer": {"mtplEnabled": true,"camera": {"eye&quo…

Linux实操——安装Mysql

安装Mysql 一、检查是否已经安装了mariadb数据库,并卸载二、下载mysql包&#xff0c;并通过ftp上传到服务器三、解压安装包四、创建数据存储文件夹五、创建执行mysqld命令的用户&#xff0c;并初始化mysql六、启用传输安全七、启动mysql&#xff0c;验证是否安装成功 总结 博主…

若依 ruoyi-vue3+ts-plus+ 宇道 yudao-ruoyi-vue-pro前端显示部门名称

想在 index.vue 上显示列表里对应的部门名称 效果 引入dept import * as DeptApi from "/api/system/dept"; 初始化时 /** 初始化 **/ onMounted(async () > {getList();deptNameList.value await DeptApi.getSimpleDeptList(); }) 加载id 对应的部门名称 …

威联通硬盘休眠后修改系统定时任务

按照网上一些教程&#xff0c;成功将威联通的机械硬盘设置了自动休眠。但是发现每天有多个整点硬盘会自动唤醒&#xff0c;怀疑是系统内置的定时任务触发了硬盘唤醒。 通过查看系统日志中事件和访问记录&#xff0c;判断出一些引发硬盘唤醒的自动任务&#xff0c;将这些定时任…