使用append之后数组维度消失_JAVA魅力之神奇的数组带给你不一样的乐趣

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

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

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

int类型数组

先来一个简单的内容,这个是正常的,以为我在日常的开发中有的时候还是能遇见的,哈哈哈哈,正不正常就以我是不是用过作为标准

public class Main {

public static void main(String[] args) {

Array score=new Array(10);

for(int i=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());

}

}

修改为泛型

public class Array {

private T[] data;

private int size;

public Array() {

this(10);

}

public Array(int capacity) {

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

data = (T[])new Object[capacity];

size = 0;

}

public int getSize() {

return size;

}

public int getCapacity() {

return data.length;

}

public boolean isEmpty() {

return size == 0;

}

/**

* 向数组末尾添加元素

*

* @param e

*/

public void addLast(T e) {

add(size, e);

}

/**

* 向数组开头添加元素

*

* @param e

*/

public void addFirst(T e) {

add(0, e);

}

/**

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

*

* @param index

* @param e

*/

public void add(int index, T e) {

if (size == data.length) {

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

}

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

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

}

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

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

}

data[index] = e;

size++;

}

/**

* 获取索引位置的元素

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

*

* @param index

* @return

*/

public T get(int index) {

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

throw new IllegalArgumentException("Get is failed.Index is illegal");

}

return data[index];

}

/**

* 修改索引位置的元素

*

* @param index

* @param e

*/

public void set(int index, T e) {

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

throw new IllegalArgumentException("Set is failed.Index is illegal");

}

data[index] = e;

}

/**

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

*

* @param e

* @return

*/

public boolean contains(T e) {

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

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

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

return true;

}

}

return false;

}

/**

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

*

* @param e

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

*/

public int find(T e) {

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

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

return i;

}

}

return -1;

}

/**

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

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

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

*

* @param index

* @return

*/

public T remove(int index) {

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

throw new IllegalArgumentException("Remove failed.Require index >=0 and index < size");

}

T ret = data[index];

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

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

}

//注意维护size

size--;

data[size]=null;

return ret;

}

/**

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

* @return

*/

public T removeFirst(){

return remove(0);

}

public T removeLast(){

return remove(size-1);

}

/**

* 从数组中删除元素e

* @param e

*/

public boolean removeElement(T e){

int index=find(e);

if(index!=-1){

remove(index);

return true;

}else{

return false;

}

}

/**

* 重写toString()方法

*

* @return

*/

@Override

public String toString() {

StringBuilder res = new StringBuilder();

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

res.append("[");

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

res.append(data[i]);

if (i != size - 1) {

res.append(",");

}

}

res.append("]");

return res.toString();

}

}

那在添加之后,我们想的就是能不能动态添加呢

增加数组大小

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

if (size == data.length) {

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

}

现在可以考虑这样做,依然判断插入位置是否合法,但是当size等于数组长度时,自动为数组扩容——resize(2*data.length);

之所以是扩为原来的2倍,是因为这样扩容量的大小和原来容量大小有关,既不会过小也不会过大。

private void resize(int newCapacity){

T[] newData=(T[])new Object[newCapacity];

for(int i=0;i

newData[i]=data[i];

}

data=newData;

}

123456

减小数组大小

因为有了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,这是不合法的。

修改后的完整代码

public class Array {

private T[] data;

private int size;

public Array() {

this(10);

}

public Array(int capacity) {

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

data = (T[])new Object[capacity];

size = 0;

}

public int getSize() {

return size;

}

public int getCapacity() {

return data.length;

}

public boolean isEmpty() {

return size == 0;

}

/**

* 向数组末尾添加元素

*

* @param e

*/

public void addLast(T e) {

add(size, e);

}

/**

* 向数组开头添加元素

*

* @param e

*/

public void addFirst(T e) {

add(0, e);

}

/**

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

*

* @param index

* @param e

*/

public void add(int index, T e) {

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

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

}*/

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

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

}

if(size == data.length){

resize(2*data.length);

}

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

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

}

data[index] = e;

size++;

}

/**

* 获取索引位置的元素

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

*

* @param index

* @return

*/

public T get(int index) {

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

throw new IllegalArgumentException("Get is failed.Index is illegal");

}

return data[index];

}

/**

* 修改索引位置的元素

*

* @param index

* @param e

*/

public void set(int index, T e) {

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

throw new IllegalArgumentException("Set is failed.Index is illegal");

}

data[index] = e;

}

/**

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

*

* @param e

* @return

*/

public boolean contains(T e) {

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

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

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

return true;

}

}

return false;

}

/**

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

*

* @param e

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

*/

public int find(T e) {

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

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

return i;

}

}

return -1;

}

/**

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

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

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

*

* @param index

* @return

*/

public T remove(int index) {

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

throw new IllegalArgumentException("Remove failed.Require index >=0 and index < size");

}

T ret = data[index];

for (int i = 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);

}

return ret;

}

/**

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

* @return

*/

public T removeFirst(){

return remove(0);

}

public T removeLast(){

return remove(size-1);

}

/**

* 从数组中删除元素e

* @param e

*/

public boolean removeElement(T e){

int index=find(e);

if(index!=-1){

remove(index);

return true;

}else{

return false;

}

}

/**

* 重写toString()方法

*

* @return

*/

@Override

public String toString() {

StringBuilder res = new StringBuilder();

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

res.append("[");

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

res.append(data[i]);

if (i != size - 1) {

res.append(",");

}

}

res.append("]");

return res.toString();

}

private void resize(int newCapacity){

T[] newData=(T[])new Object[newCapacity];

for(int i=0;i

newData[i]=data[i];

}

data=newData;

}

}

推荐

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

4b686ae0569368ce0aa2617771f17301.png
  • 好了,基本就整理了这一些,我不会说我就想到了这一些的,毕竟一个人的想法还是有限的,希望大家能够在下面的评论区和我讨论,大家一起学习成长,关注我,一个脑回路清奇的程序员
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值