2. 基础数据结构-数组

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的遍历方式更适合计算机硬件的工作原理,因此执行速度更快。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode—113.路径总和II【中等】

2023每日刷题&#xff08;五十七&#xff09; Leetcode—113.路径总和II 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* …

【数谷·企声】贵州恩典集团:半年内实现上规入统,大力推广贵州酱酒品牌

近年来&#xff0c;贵阳贵安大力实施“数字活市”战略&#xff0c;数字产业高速增长&#xff0c;数字红利加速释放&#xff0c;营商环境持续优化&#xff0c;成功吸引了一批批优质企业落户&#xff0c;贵州恩典企业管理&#xff08;集团&#xff09;有限公司&#xff08;以下简…

DICOM 文件中,VR,VL,SQ,图像二进制的几个注意点

DICOM 文件的结构&#xff0c;在网上有很多的学习资料&#xff0c;这里只介绍些容易混淆的概念&#xff0c;作为回看笔记。 1. 传输语法 每个传输语法&#xff0c;起都是表达的三个概念&#xff1a;大小端、显隐式、压缩算法 DICOM Implicit VR Little Endian: 1.2.840.1000…

Linux 常用的操作命令

我们习惯的使用Windows,安装软件进行使用&#xff0c;比如 WPS&#xff0c;浏览器&#xff0c;一些工具&#xff0c;但是在Linux上就需要用命令去操作&#xff0c;也可以使用像Ubuntu 和 CentOS这类的可视化面板 Linux系统是开源的&#xff0c;所以开发人员可以反复的发现Bug以…

高项备考葵花宝典-项目进度管理核心方法加强理解-关键路径法

关键路径法&#xff08;Critical Path Method&#xff0c;CPM&#xff09;是一种基于数学计算的项目计划管理方法&#xff0c;是网络图计划方法的一种&#xff0c;属于肯定型的网络图。关键路径法将项目分解成为多个独立的活动并确定每个活动的工期&#xff0c;然后用逻辑关系&…

​hashlib --- 安全哈希与消息摘要​

源码&#xff1a; Lib/hashlib.py 本模块针对许多不同的安全哈希和消息摘要算法实现了一个通用接口。 包括了 FIPS 安全哈希算法 SHA1, SHA224, SHA256, SHA384, SHA512, (定义见 the FIPS 180-4 standard), SHA-3 系列 (定义见 the FIPS 202 standard) 以及 RSA 的 MD5 算法 (…

首场“解数Talk” 直播来了——大模型语料数据联盟开源数据集解读

一、解数 Talk 介绍 为帮助广大开发者更好地了解大模型语料数据联盟发布的AI大模型语料数据&#xff0c;沟通大模型企业在AI视角下的数据需求&#xff0c;不断服务大模型产业生态和落地应用&#xff0c;联盟发起单位上海人工智能实验室联合成员单位共同打造“解数 Talk”系列直…

智能优化算法应用:基于引力搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于引力搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于引力搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.引力搜索算法4.实验参数设定5.算法结果6.…

Vue中发送Ajax请求的方式axios及其跨域问题的解决方案代理机制的配置和原理

Vue中的Ajax请求 现在比较流行的开发方式为异步调用,前后台以异步Ajax请求的方式进行交换数据,传输的数据使用的是JSON Ajax请求发送后,当浏览器接收到服务器的响应内容后不会重新加载整个页面,只会更新网页的部分实现局部刷新的效果 发送AJAX异步请求的常见方式包括 使用浏…

Java 对接企业微信(文本消息推送)

Java 对接企业微信&#xff08;文本消息推送&#xff09; 背景版本代码POM配置实体工具类发送消息测试配置文件配置文件中的参数来源secretcorpidagentid 执行异常原因 文档 背景 公司的项目&#xff0c;通知信息打算接入企业微信通知。提前做下实验。 版本 JDK 21 SpringBoo…

故障排查方法与技巧

判断网络是否稳定&#xff0c;最重要的两个命令 ping 10.28.0.23 -t -l 1000 -t &#xff1a;无限循环ping -l&#xff1a;指定数据包大小 内网环境< 1ms,是较好的网络&#xff0c;如果跳到100多&#xff0c;说明网络不稳定 telnet ip地址空格端口号 表示不通 数据库…

老师发成绩单攻略:5种方法让群发成绩变得更高效

作为老师&#xff0c;发布成绩单是一项重要的任务。为了更高效地完成这项任务&#xff0c;本文将介绍5种方法&#xff0c;帮助老师群发成绩单更加高效。 一、提前规划&#xff0c;做好准备 在发布成绩单之前&#xff0c;老师需要提前规划好发布的时间、方式、接收对象等&#…

路径总和(递归)

112. 路径总和 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &a…

34、卷积实战 - 手写一个基础卷积算法

前面基本上把卷积这一算法的原理和公式介绍完了,如果还有不懂的,可以多翻几遍前面的章节内容,深入理解一下。 本节加一个实战,大家可以手动来实现一个卷积算法,本文中以 python 代码为例,C++ 的代码可以查看本节后面的链接。 说到卷积实现,其实就是自己手写一个卷积算…

js基础:简介、变量与数据类型、流程循环控制语句、数组及其api

JS基础&#xff1a;简介、变量与数据类型、流程循环控制语句、数组及其api 一、简介 1、js概述 tip&#xff1a;JavaScript是什么&#xff1f; 有什么作用&#xff1f; JavaScript&#xff08;简称JS&#xff09;是一种轻量级的、解释性的编程语言&#xff0c;主要用于在网页…

SpringCloud微服务(简略笔记二)

Docker 概念 docker和虚拟机的差异 * docker是一个系统进程&#xff1b;虚拟机是在操作系统中的操作系统 * docker体积小&#xff0c;启动速度&#xff0c;性能好&#xff0c;虚拟机体积大&#xff0c;启动速度慢&#xff0c;性能一般 镜像和容器 镜像&#xff08;image&…

多模态统计图表综述:图表分类,图表理解,图表生成,图表大一统模型

Overview 多模态统计图表综述一、图表分类1.1 Survey1.2 常见分类数据集&#xff1a;1.3 常见图表类型 二、图表理解2.1 VQA2..1.1 DVQA CVPR20182.1.2 PlotQA 20192.1.3 ChartQA 2022 2.2 Summary2.2.1 Chart-to-text ACL 2022 三、图表生成四、图表大一统模型4.1 UniChart 20…

stm32 使用18B20 测试温度

用18b20 测试温度是非常常用的&#xff0c;不过18B20的调试不是这么容易的&#xff0c;有些内容网上很多的&#xff0c;不再重复说了&#xff0c;我先把波形说一下&#xff0c;再说程序部分&#xff1a; 整个都温度数据的顺序是&#xff1a; 1.700uS的低电平复位并测试18B20的…

【matlab进阶学习-6】 读取log数据data.txt文件,并做处理,导出报告/表格/图表

原始文件 原始文件格式txt&#xff0c;每一行对应一个数据&#xff0c;数据之间由逗号分割开 对应意思 时刻&#xff0c;电压&#xff0c;电流&#xff0c;功率&#xff0c;容量&#xff0c;&#xff0c;电流&#xff0c;功率&#xff0c;&#xff0c;RTC时间&#xff0c;状态…

什么是电商价格监控

电商价格监控是一种系统爬取数据后的实现动作&#xff0c;比起含义&#xff0c;其实更应该关注为什么要做电商价格监控&#xff0c;电商价格监控的执行前提是品牌要治理渠道&#xff0c;需要将电商平台的低价链接打击干净&#xff0c;那就需要先对低价链接进行确认、筛选&#…