【数据结构与算法-java实现】三 Java数组类实现

  • 上一篇文章学习了:最好、最坏、平均、均摊时间复杂度的计算与分析方法.
  • 本片文章学习数组这种结构。由于数组这种结构比较简单,本文直接简单介绍,然后给出两种实现数组类的Java代码:整形数组类与通用性的数组类

由于数组是相比于其他数据结构实在太简单,这里我们只做简单的介绍,然后直接给出实现的代码。

文章目录

  • 1 数组的概念
    • 1.1 Java中数组的越界访问
  • 2 整形数组类的实现
  • 3 通用型数组类的实现
  • 4 总结

1 数组的概念

数组(Array)是一种线性表数据结构。它用一组连续的内存空,来存储一组具有相同类型的数据。对于线性表和非线性表这里不再多说。

我们要注意数组它是连续的内存空间和相同的数据类型。

还有一点就是我们要明白数组是如何做到根据下标来访问数组的元素的。我们拿一个长度为10的int类型的数组int[] a=new int[10];来说明。如下图是申请内存后数组元素的存储方式。

在这里插入图片描述

假设上述数组的起始地址为1000.当CPU要访问某一个数组的元素时,将会通过下面的公式进行访问元素:

a[i]_address = base_address + i * data_type_size

其中,base_address 为数组的起始地址。data_type_size为数组元素的类型的大小。

计算机就是通过上述的公式进行数组的随机访问的。不像链表等非线性结构,无法随机访问元素。

  • 数组这种结构,虽然具有随机访问的高效性,但是它的插入和删除确实非常的低效。
  • 以及数组的访问也是很容易越界的

以上插入和删除的低效,这里不再赘述。

我们注意一下数组访问的越界。

1.1 Java中数组的越界访问

在讲述Java中数组的越界访问之前,先来看一下C语言的越界访问。如下代码:

int main(int argc, char* argv[]){int i = 0;int arr[3] = {0};for(; i<=3; i++){arr[i] = 0;printf("hello world\n");}return 0;
}

上述代码运行后,并非是打印3次hello world,而是无限次的打印hello world。这是为什么?

数组arr的大小是3,但是上面的for循环由于书写错误,访问了arr[3],这就是越界访问。在C语言中,只要不是访问内存受限的地方,所有的内存都是可以访问的。所以由上面的数组访问的公式,a[3]也被定位到一块内存上,而这块内存中存储的刚好是局部变量i。那么arr[3]实际上就是i,这就导致此时又将i赋值为0。此时回到for循环发现i是0,继续循环。。。。。

数组越界在C语言中是一种未决行为,并没有规定数组越界访问时应该如何处理。因为数组访问的本质是访问一段连续的内存,只要数组通过偏移计算得到的内存是可用的,那么程序就可能不会报任何错误。C语言中这种行为,很难debug到错误。

但是不像c语言,Java语言不允许这种越界访问。Java编译器会做越界检查,如果有越界访问,将会抛出异常。如:java.lang.ArrayIndexOutOfBoundsException。

2 整形数组类的实现

由于数组比较简单,上面的内容我觉的就够了。下面直接给出int数组类的实现。可以直接在eclipse编译运行的代码。代码如下:

  • Array.java
package Array;/*** 1) 数组的插入、删除、按照下标随机访问操作;* 2)数组中的数据是int类型的;**/public class Array {//定义整形数据保存数据public int data[];//数组的长度private int len;//数组中的实际个数private int cnt;//构造方法,定义数组大小public Array(int capacity) {this.data=new int[capacity];this.len=capacity;this.cnt=0;//一开始一个数据都诶呦,所以为0}//根据索引找到数组中的元素并返回public int find(int index) {if(index<0 || index>=len)return -1;return data[index];}//插入元素,包括头部插入,尾部插入,中间插入public boolean insert(int index, int value) {//数组空间已满if(cnt==len) {System.out.println("数组空间已满,无法插入!");return false;}//插入位置不合法if(index<0 || index>=len) {System.out.println("插入位置不合法!");return false;}//位置合法for(int i=cnt;i>index;--i) {data[i]=data[i-1];}data[index]=value;++cnt;return true;}//根据索引,删除数组中的元素public boolean delete(int index) {if(index<0 || index>=len)return false;//从删除的位置开始,将后面的元素向前移动一位for(int i=index+1;i<len;i++) {data[i-1]=data[i];}--cnt;return true;}public void printAll() {for(int i=0;i<len;i++) {System.out.print(data[i]+" ");}System.out.println();}public static void main(String args[]) {Array array = new Array(5);array.printAll();array.insert(0,3);array.insert(0, 4);array.insert(1, 5);array.insert(2, 2);array.insert(3, 6);array.insert(4, 8);array.printAll();}
}
  • 运行结果如下:
    在这里插入图片描述

以上代码只实现了数组类的插入和删除。更多的方法在下面的通用性数组的实现里面。

3 通用型数组类的实现

下面是实现通用的数组类,也就是大家所知的泛型。C++中叫做模板。

下面的代码已经经过测试,是可以正常使用的。如果你发现有其他bug,请在下方留言评论。

代码如下:

  • GenericArray.java
package Array;//这个可能你的不一样public class GenericArray<T>{private T[] data;private int size;//构造函数public GenericArray(int capacity) {data = (T[])new Object[capacity];size=0;}//无参构造方法,默认数组容量为10public GenericArray() {this(10);}//获取数组容量public int getCapacity() {return data.length;}//获取当前元素个数public int count() {return size;}//判断数组是否为空public boolean isEmpty() {return size==0;}//修改index位置的元素public void set(int index, T e) {checkIndex(index);data[index]=e;}//获取对应index位置的元素public T get(int index) {checkIndex(index);return data[index];}//查看数组是否包含元素public boolean contains(T e) {for(int i=0;i<this.size;++i) {if(data[i].equals(e))return true;}return false;}//获取对应元素的下标,未找到则返回-1public int find(T e) {for(int i=0;i<this.size;++i) {if(data[i].equals(e))return i;}return -1;}//在index位置插入元素e,时间复杂度是O(m+n)public void add(int index, T e) {checkIndex(index);//如果当前元素的个数等于数组的容量,则将数组扩容为原来的两倍if(size==data.length) {resize(2*data.length);}for(int i=size-1;i>=index;--i) {data[i+1]=data[i];}data[index]=e;size++;}//向数组头插入元素public void addFirst(T e) {add(0, e);}//向数组尾插入元素public void addLast(T e) {add(size, e);}// 删除index位置的元素并返回public T remove(int index) {chaeckIndexForRemove(index);T ret =data[index];for(int i=index+1;i<size;++i) {data[i-1]=data[i];}--size;data[size]=null;//缩小数组容量if(size == (data.length/4) && (data.length/2)!=0) {resize(data.length/2);}return ret;}//删除第一个元素public T removeFirst() {return remove(0);}//删除末尾元素public T removeLast() {return remove(size-1);}//从数组中删除指定的元素public void removeElement(T e) {int index=find(e);if(index!=-1) {remove(index);}}@Overridepublic String toString() {StringBuilder builder = new StringBuilder();builder.append(String.format("Array size = %d, capacity = %d \n", size, data.length));builder.append('[');for (int i = 0; i < size; i++) {builder.append(data[i]);if (i != size - 1) {builder.append(", ");}}builder.append(']');return builder.toString();}private void chaeckIndexForRemove(int index) {// TODO Auto-generated method stubif(index < 0 || index >= size) {throw new IllegalArgumentException("remove failed! Require index >=0 and index < size.");}}//扩容方法,时间复杂度O(n)private void resize(int capacity) {// TODO Auto-generated method stubT[] newData= (T[])new Object[capacity];for(int i=0;i<size;++i) {newData[i]=data[i];}data=newData;}private void checkIndex(int index) {// TODO Auto-generated method stubif(index<0 || index>size) {throw new IllegalArgumentException("Add failed! Require index >=0 and index <= size.");}}//测试用的main,你可以自己写测试函数public static void main(String args[]) {GenericArray<Integer> a = new GenericArray<Integer>(5);a.add(0, 2);a.add(1, 4);a.add(2, 3);a.add(3, 7);a.add(4, 9);for(int i=0;i<a.size;++i) {System.out.print(a.get(i)+" ");}System.out.println();a.remove(1);for(int i=0;i<a.size;++i) {System.out.print(a.get(i)+" ");}a.addFirst(23);System.out.println();for(int i=0;i<a.size;++i) {System.out.print(a.get(i)+" ");}a.addLast(24);System.out.println();for(int i=0;i<a.size;++i) {System.out.print(a.get(i)+" ");}}
}
  • 上述代码在eclipse中运行结果如下:

在这里插入图片描述

4 总结

  • 注意数组的插入删除的效率以及越界访问

学习交流加

  • 个人qq: 1126137994
  • 个人微信: liu1126137994
  • 学习交流资源分享qq群: 962535112

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

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

相关文章

解决VMware6.5 以上版本安装RHEL 5的自动安装的问题

解决VMware6.5 以上版本安装RHEL 5的自动安装的问题记得在学习 RHEL5的时候&#xff0c;教室里用的VMware5.5.3的版本&#xff0c;在教室里安装的时候是很正常的有步骤的那种&#xff0c;回到寝室后&#xff0c;用我的VMware6.5 安装的时候却发现&#xff0c;完全变成了自动安装…

【软件开发底层知识修炼】二十一 ABI-应用程序二进制接口一

前面学习了可执行程序的结构&#xff0c;点击链接查看上一篇文章&#xff1a;【软件开发底层知识修炼】二十 深入理解可执行程序的结构本篇文章开始新的篇章&#xff0c;学习应用程序的二进制接口-ABI。 文章目录1 什么是ABI&#xff08;Application Binary Interface&#xff…

【软件开发底层知识修炼】二十二 ABI-应用程序二进制接口 二

上一篇文章学习了ABI的相关内容&#xff0c;具体最后分析了不同ABI下结构体的对齐方式的不同。点击链接查看上一篇文章&#xff1a;【软件开发底层知识修炼】二十一 ABI-应用程序二进制接口一本篇文章继续学习ABI相关内容。是上一篇文章的补充&#xff0c;如果没有看过上一篇文…

asp.net中RegularExpressionValidator控件中正则表达式用法

验证数字&#xff1a; 只能输入1个数字 表达式 ^\d$ 描述 匹配一个数字 匹配的例子 0,1,2,3 不匹配的例子 只能输入n个数字 表达式 ^\d{n}$ 例如^\d{8}$ 描述 匹配8个数字 匹配的例子 12345678,22223334,12344321 不匹配的例子 只能输入至少n个数字 表达式 ^\d{n,}$ 例如^\d{8…

VS2017社区版30天到期无法使用的激活方法

VS2017社区版30天到期无法使用的激活方法VS2017社区版是免费的&#xff0c;但是第一次安装时&#xff0c;没有登录&#xff0c;导致只要30天的试用期&#xff0c;现在试用期结束&#xff0c;无法使用&#xff0c;本教程就是解决这个问题。我先在控制面板中将2017社区版软件卸载…

五分钟搞懂内网和外网之间的通信的原理

写的通熟易懂&#xff0c;特转过来备忘&#xff01;原创链接找不到了 对于初学者而已&#xff0c;我们学习的网络编程(如TCP,UDP编程)&#xff0c;我们通常都是在局域网内进行通信测试&#xff0c;有时候我们或者会想&#xff0c;我们现在写的内网网络数据和外网的网络数据有什…

【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁

上两篇文章我们初步接触了ABI-应用程序二进制接口的概念&#xff0c;点击链接查看上一篇文章&#xff1a;【软件开发底层知识修炼】二十二 ABI-应用程序二进制接口 二。了解了为什么会有ABI的存在。本篇文章继续学习ABI 的内容。学习在ABI规范下&#xff0c;函数栈帧的结构与函…

【转】电脑GPS导航软件下载,教你把笔记本做成GPS

在开始之前&#xff0c;先说一下&#xff0c;相信很多朋友在谷哥搜索“电脑GPS导航软件”时&#xff0c;都很难找到真正的下载地址&#xff0c;多数是只能下载到灵图的破解文件&#xff0c;那么&#xff0c;本文不同&#xff0c;本文不但教你怎么打造电脑GPS&#xff0c;而且提…

【软件开发底层知识修炼】二十四 ABI之函数调用约定

上一篇文章学习了Linux环境下的函数栈帧的形成与摧毁。点击链接查看相关文章&#xff1a;软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁本篇文章继续学习ABI接口相关的内容。函数调用约定 文章目录1 函数参数如何入栈&#xff0c;返回…

【软件开发底层知识修炼】二十五 ABI之函数调用约定二之函数返回值为结构体时的约定

上一篇文章学习了几种函数调用约定的区别&#xff0c;点击链接查看上一篇文章&#xff1a;【软件开发底层知识修炼】二十四 ABI之函数调用约定本篇文章继续学习函数调用约定中&#xff0c;关于函数返回值的问题。当函数返回值为结构体时&#xff0c;函数返回值是如何来传给调用…

CSDN-Markdown-图片设置(大小,居中)

利用markdown在编写文档时插入图片是默认靠左&#xff0c;有些时候将图片设置为居中时可以更加的美观&#xff0c;这时就需要在图片的信息前边添加如下程序 <div aligncenter>![这里写图片描述](http:...如果想将图片位于右侧&#xff0c;只需要将center改为right<di…

CSDN中markdown字体颜色,大小,首行缩进,居中排布

一、下面是首行缩进的两种方法 1.这里实用空格去替代缩进的字符&#xff0c;下面讲的替代包括分号 2.把输入法由半角改为全角。 两次空格之后就能够有两个汉字的缩进。 半方大的空白用&ensp;或 全方大的空白用&emsp;或 不断行的空白格用 或 示例&#xff1a; 略略略…

使用VNC软件与花生壳进行内网穿透实现在嵌入式平台中进行广域网下的远程控制

在嵌入式平台中如何实现广域网下的远程登录控制&#xff1f; 文章目录1 项目需要2 解决方案3 首先实现局域网下的VNC远程控制4 总结1 项目需要 在IM.X6q硬件平台&#xff0c;Linux4.1.15内核版本中实现在广域网下进行远程登录控制。主控机为Windows机器&#xff0c;被控机是IM…

【软件开发底层知识修炼】二十七 C/C++中的指针与数组是不同的

上几篇文章学习了ABI-应用程序二进制接口&#xff1a;【软件开发底层知识修炼】二十六 ABI-应用程序二进制接口 学习总结文章目录本篇文章就指针与数组的联系与区别来学习学习 文章目录1 疑问2 指针与数组是不相等的3 解决疑问4 总结1 疑问 在具体用文字理论来说明指针与数组的…

微软MIX11大会第一天主旨以及新产品发布总结

期盼已久的MIX11终于开幕了&#xff0c;虽然没有去现场&#xff0c;但是心情还是蛮激动的。 MIX11第一天Keynote实况大概1个多小时&#xff0c;其中介绍了下一代微软浏览器&#xff0c;新工具更新以及新产品发布等&#xff0c;下面总结一下MIX11第一天的主要话题。 1. Internet…

【Git、GitHub、GitLab】三 Git基本命令之创建仓库并向仓库中添加文件

前两篇文章已经学会了Git的基本命令与创建仓库的命令&#xff0c;点击链接查看上一篇文章&#xff1a;【Git、GitHub、GitLab】二 Git基本命令之建立Git仓库&#xff0c;本篇文章就来创建一个有模有样的仓库。该仓库中的代码是一个显示静态页面的小工程代码。 文章目录0 本文所…