【数据结构与算法-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;完全变成了自动安装…

远程桌面复制文件,由于网络或其他原因被意外中断,后来再连上远程桌面就无法复制了,而且复制文件的对话框也无法取消,可以试试下面的方法,实测有效:

1&#xff1a;打开任务管理器 2&#xff1a;找到结束进程rdpclip&#xff0c;找不到可以不管。 3&#xff1a;手工新建任务里输入rdpclip&#xff0c;运行即可。

TreeTemplate树模板

将创建树形数据结构抽象出功能类 1   usingSystem.Collections.Generic;2   usingSystem;3 4   publicclassTreeTemplate<T>5   {6     privatereadonlyList<T>_data newList<T>();7 8   publicTreeTemplate(List<T>items)9   {10   …

【软件开发底层知识修炼】二十一 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;前提当然是必须掌握基本的软件技术和了解专业的理论知识等。以下是迅速提高设计水平的九大秘诀。一&#xff0c;想设计师首先必须善于思考&#xff0c;对设计主张自己的想法和理解&#xff0c;这是最重要的。别担心你的想法有多“烂”&#xff0c;没…

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

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

Linux设备驱动程序学习(4) -高级字符驱动程序操作[(1)ioctl and llseek]

今天进入《Linux设备驱动程序&#xff08;第3版&#xff09;》第六章高级字符驱动程序操作的学习。 一、ioctl 大部分设备除了读写能力&#xff0c;还可进行超出简单的数据传输之外的操作&#xff0c;所以设备驱动也必须具备进行各种硬件控制操作的能力. 这些操作常常通过 io…

markdown文本居中方法(CSDN博客编辑器文本居中方法)

居中居中使用html添加&#xff1a;   <center>这一行需要居中</center> 比如: <center>居中</cenetr>对应下面&#xff1a; 居中

【软件开发底层知识修炼】二十三 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;返回…

深入了解 Python 的 import 语句

在 Python 中&#xff0c;import 语句是一个关键的功能&#xff0c;用于在程序中引入模块和包。本文将深入讨论 import 语句的各种用法、注意事项以及一些高级技巧&#xff0c;以帮助你更好地理解和使用这一功能。 概念介绍 package 通常对应一个文件夹&#xff0c;下面可以有…

javascript 获取上一页的url

在js中&#xff0c;如果通过连接或者表单提交里&#xff0c;可以用以下三种方式获取上一页的url&#xff1a; 1、document.referrer 2、top.document.referrer 3、window.parent.document.referrer 这在ie和firefox里都可以实现 但如果在IE中用js函数跳转的话&#xff0c;以上三…

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

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

说说我最近比较迷的Criminal Minds

喜欢《犯罪心理》这部美剧很久了&#xff0c;从第四季一直到现在还在追的第六季&#xff0c;觉得集集精彩。简练而不失悬念。该片的一些情节上的特质让我总是与工作上的某些场景产生对比联想。管理模式。BAU是一个典型的家庭式的管理方式。Hotch作为大家长&#xff0c;不辞辛劳…

【软件开发底层知识修炼】二十六 ABI-应用程序二进制接口 学习总结文章目录

前面学习了ABI的知识&#xff0c;感觉受益良多。对底层与编译器有更加深刻的认识&#xff0c;为此这里将前面写过的关于ABI 的文章给列出来&#xff0c;方便学习与翻阅。 【软件开发底层知识修炼】二十一 ABI-应用程序二进制接口一【软件开发底层知识修炼】二十二 ABI-应用程序…

关于《高性能JavaScript》制表(Memoization)笔记

减少工作量是最好的性能优化技术。代码所做的事情越少&#xff0c;他的速度就越快。 书中在关于用制表优化递归的下一段代码中&#xff0c;存在问题。 function memfactorial(n){ if (!memfactorial.cache){ memfactorial.cache { "0": 1, …