JAVA魅力之神奇的数组带给你不一样的乐趣

咱也不知道咋想的,周末的的时候闲来没事,因为看到一道数组的面试题,我就跟数组杠上了,问题也不大,就是一个数组重写的问题,我不知道大家有没有遇到过类似的问题,在日常的开发环境中,反正我是没遇到过,正常的开发就行,可能是因为我公司业务的原因吧,哈哈哈哈

但是,我个人的爱好就是会看一些面试题,去考察自己的知识点掌握情况,以及和市场的动态,建议大家也可以这样试一下,保持自己的竞争力,所以,在看到这道面试题之后,我就想去试一下,然后就有了下面的代码详解

看正式代码之前,关注我给我点动力吧,让我能更好的往下创作,

个人公众号:Java架构师联盟,每日更新技术好文

嘿嘿嘿,好了,说正事

int类型数组

publicclassMain{

publicstaticvoidmain(String[]args) {

Arrayscore=newArray(10);

for(inti=0;i<8;i++){

score.addLast(i);

       }

score.add(1,100);

score.addLast(99);

System.out.println(score.toString());

System.out.println(score.find(1));

System.out.println(score.contains(1));

System.out.println(score.remove(1));

System.out.println(score.toString());

System.out.println(score.removeElement(2));

System.out.println(score.toString());

   }

}

修改为泛型

publicclassArray<T>{

privateT[]data;

privateintsize;

publicArray() {

this(10);

   }

publicArray(intcapacity) {

//java本身不支持直接new 一个泛型数组,所以用以下方法实现

data=(T[])newObject[capacity];

size=0;

   }

publicintgetSize() {

returnsize;

   }

publicintgetCapacity() {

returndata.length;

   }

publicbooleanisEmpty() {

returnsize==0;

   }

/**

* 向数组末尾添加元素

*

* @param e

*/

publicvoidaddLast(Te) {

add(size,e);

   }

/**

* 向数组开头添加元素

*

* @param e

*/

publicvoidaddFirst(Te) {

add(0,e);

   }

/**

* 向任意合法位置添加元素

*

* @param index

* @param e

*/

publicvoidadd(intindex,Te) {

if(size==data.length) {

thrownewIllegalArgumentException("Add is fail.Array is full");

       }

if(index<0||index>size) {

thrownewIllegalArgumentException("Add is fail.Require index >= 0 and index < size");

       }

for(inti=size;i>index;i--) {

data[i]=data[i-1];

       }

data[index]=e;

size++;

   }

/**

* 获取索引位置的元素

* 通过这种封装,用户无法查询未使用的空间,保证了数据的安全性。

*

* @param index

* @return

*/

publicTget(intindex) {

if(index<0||index>=size) {

thrownewIllegalArgumentException("Get is failed.Index is illegal");

       }

returndata[index];

   }

/**

* 修改索引位置的元素

*

* @param index

* @param e

*/

publicvoidset(intindex,Te) {

if(index<0||index>=size) {

thrownewIllegalArgumentException("Set is failed.Index is illegal");

       }

data[index]=e;

   }

/**

* 数组中是否包含某个元素

*

* @param e

* @return

*/

publicbooleancontains(Te) {

for(inti=0;i<size;i++) {

//注意值的比较应修改为equals方法

if(data[i].equals(e)) {

returntrue;

           }

       }

returnfalse;

   }

/**

* 查询数组中某个元素的索引

*

* @param e

* @return 没有该元素则返回-1

*/

publicintfind(Te) {

for(inti=0;i<size;i++) {

if(data[i].equals(e)) {

returni;

           }

       }

return-1;

   }

/**

* 删除index位置的元素,并且返回该元素

* 不用担心删除后原来size位置的元素,因为用无法访问到它。

* 但是最好再写一句data[size]=null 具体原因需要了解java的垃圾回收机制

*

* @param index

* @return

*/

publicTremove(intindex) {

if(index<0||index>=size) {

thrownewIllegalArgumentException("Remove failed.Require index >=0 and index < size");

       }

Tret=data[index];

for(inti=index;i<size-1;i++) {

data[i]=data[i+1];

       }

//注意维护size

size--;

data[size]=null;

returnret;

   }

/**

* 不用担心数组为空的情况,因为数组如果为空,remove方法就会抛出异常

* @return

*/

publicTremoveFirst(){

returnremove(0);

   }

publicTremoveLast(){

returnremove(size-1);

   }

/**

* 从数组中删除元素e

* @param e

*/

publicbooleanremoveElement(Te){

intindex=find(e);

if(index!=-1){

remove(index);

returntrue;

}else{

returnfalse;

       }

   }

/**

* 重写toString()方法

*

* @return

*/

@Override

publicStringtoString() {

StringBuilderres=newStringBuilder();

res.append(String.format("size= %d,capacity= %d\n",size,data.length));

res.append("[");

for(inti=0;i<size;i++) {

res.append(data[i]);

if(i!=size-1) {

res.append(",");

           }

       }

res.append("]");

returnres.toString();

   }

}

增加数组大小

前面当我们向数组中添加元素时,如果index==size,表示数组已满。

if(size==data.length) {

thrownewIllegalArgumentException("Add is fail.Array is full");

}

现在可以考虑这样做,依然判断插入位置是否合法,但是当size等于数组长度时,自动为数组扩容——resize(2*data.length);之所以是扩为原来的2倍,是因为这样扩容量的大小和原来容量大小有关,既不会过小也不会过大。

privatevoidresize(intnewCapacity){

T[]newData=(T[])newObject[newCapacity];

for(inti=0;i<size;i++){

newData[i]=data[i];

   }

data=newData;

}

减小数组大小

因为有了resize方法,实现起来就很简单了。在remove(int index)方法中,移除一个元素且维护size后,再加上对维护后的size的判断,如下。如果size已经变为capacity的一半,则将数组容量减半。

if(size==data.length/2){

resize(data.length/2);

}

注意resize()方法设为私有,是为了用户只需使用这个数组类,不必去顾虑数组的大小。

时间复杂度的分析

通过对addLast(T[] e)和removeLast(T[] e)时间复杂度的分析,我们发现都是O(n)级别的。但是,这样一般性的考虑最坏的情况在这种情景下是没有太大意义的。因为addLast(T[] e)和removeLast(T[] e)操作并不会经常触发resize(int newCapcity)操作。所以用均摊复杂度分析的话,你会发现这两个操作的均摊复杂度都是O(1)。因此resize(int newCapcity)这样一个比较耗时的操作,如果保证不会每次都会触发,就可以将它的操作耗时分摊到其他操作上。

现在再考虑另外一个场景,就是在addLast(T[] e)操作后,触发了resize(int newCapcity),然后再removeLast(T[] e),又触发了resize(int newCapcity);如此循环。像这样addLast(T[] e)和removeLast(T[] e)的时间复杂度都是O(n)级别的,这就是所谓的复杂度的震荡。以数组这个例子,之所以发生这种情况是因为我们在removeLast操作后,就接着进行了resize操作这样太着急了。那么该如何防止复杂度的震荡呢?可以这样修改removeLast方法的代码。

if(size==data.length/4&&data.length/4!=0){

    resize(data.length/2);

}

加上data.length/4!=0的判断是因为当data.length/4==0的时候,数组长度变为0,这是不合法的。

修改后的完整代码

publicclassArray<T>{

privateT[]data;

privateintsize;

publicArray() {

this(10);

   }

publicArray(intcapacity) {

//java本身不支持直接new 一个泛型数组,所以用以下方法实现

data=(T[])newObject[capacity];

size=0;

   }

publicintgetSize() {

returnsize;

   }

publicintgetCapacity() {

returndata.length;

   }

publicbooleanisEmpty() {

returnsize==0;

   }

/**

* 向数组末尾添加元素

*

* @param e

*/

publicvoidaddLast(Te) {

add(size,e);

   }

/**

* 向数组开头添加元素

*

* @param e

*/

publicvoidaddFirst(Te) {

add(0,e);

   }

/**

* 向任意合法位置添加元素

*

* @param index

* @param e

*/

publicvoidadd(intindex,Te) {

/*if (size == data.length) {

throw new IllegalArgumentException("Add is fail.Array is full");

}*/

if(index<0||index>size) {

thrownewIllegalArgumentException("Add is fail.Require index >= 0 and index < size");

       }

if(size==data.length){

resize(2*data.length);

       }

for(inti=size;i>index;i--) {

data[i]=data[i-1];

       }

data[index]=e;

size++;

   }

/**

* 获取索引位置的元素

* 通过这种封装,用户无法查询未使用的空间,保证了数据的安全性。

*

* @param index

* @return

*/

publicTget(intindex) {

if(index<0||index>=size) {

thrownewIllegalArgumentException("Get is failed.Index is illegal");

       }

returndata[index];

   }

/**

* 修改索引位置的元素

*

* @param index

* @param e

*/

publicvoidset(intindex,Te) {

if(index<0||index>=size) {

thrownewIllegalArgumentException("Set is failed.Index is illegal");

       }

data[index]=e;

   }

/**

* 数组中是否包含某个元素

*

* @param e

* @return

*/

publicbooleancontains(Te) {

for(inti=0;i<size;i++) {

//注意值的比较应修改为equals方法

if(data[i].equals(e)) {

returntrue;

           }

       }

returnfalse;

   }

/**

* 查询数组中某个元素的索引

*

* @param e

* @return 没有该元素则返回-1

*/

publicintfind(Te) {

for(inti=0;i<size;i++) {

if(data[i].equals(e)) {

returni;

           }

       }

return-1;

   }

/**

* 删除index位置的元素,并且返回该元素

* 不用担心删除后原来size位置的元素,因为用无法访问到它。

* 但是最好再写一句data[size]=null 具体原因需要了解java的垃圾回收机制

*

* @param index

* @return

*/

publicTremove(intindex) {

if(index<0||index>=size) {

thrownewIllegalArgumentException("Remove failed.Require index >=0 and index < size");

       }

Tret=data[index];

for(inti=index;i<size-1;i++) {

data[i]=data[i+1];

       }

//注意维护size

size--;

data[size]=null;

if(size==data.length/4&&data.length/4!=0){

    resize(data.length/2);

       }

returnret;

   }

/**

* 不用担心数组为空的情况,因为数组如果为空,remove方法就会抛出异常

* @return

*/

publicTremoveFirst(){

returnremove(0);

   }

publicTremoveLast(){

returnremove(size-1);

   }

/**

* 从数组中删除元素e

* @param e

*/

publicbooleanremoveElement(Te){

intindex=find(e);

if(index!=-1){

remove(index);

returntrue;

}else{

returnfalse;

       }

   }

/**

* 重写toString()方法

*

* @return

*/

@Override

publicStringtoString() {

StringBuilderres=newStringBuilder();

res.append(String.format("size= %d,capacity= %d\n",size,data.length));

res.append("[");

for(inti=0;i<size;i++) {

res.append(data[i]);

if(i!=size-1) {

res.append(",");

           }

       }

res.append("]");

returnres.toString();

   }

privatevoidresize(intnewCapacity){

T[]newData=(T[])newObject[newCapacity];

for(inti=0;i<size;i++){

newData[i]=data[i];

       }

data=newData;

   }

}

推荐

最后忍不住展示一下markdown写作软件的截图,因为它实在是太美了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342