数组基础:
1 package cn.zcb.demo01; 2 3 public class Test { 4 public static void main(String[] args) { 5 //数组创建 和 初始化的两种方式 6 //1 7 int [] arr = new int[10]; 8 for (int i=0;i<arr.length ;i++){ 9 arr[i] = i; 10 } 11 //2 12 int [] scores = new int[] {10,20,30,40}; 13 14 //下面是两种打印方式 15 //1 16 for (int i=0;i<scores.length;i++){ 17 System.out.println(scores[i]); 18 } 19 //2 20 for (int score:scores){ 21 System.out.println(score); 22 } 23 24 25 } 26 } 27 /* 输出结果 28 10 29 20 30 30 31 40 32 10 33 20 34 30 35 40 36 * */
索引的语义:
所以可以有语义,也可以没有语义,
有语义,例如0 代表学号,
没有语义,例如0 只是代表第一个而已。
数组最大的优点是:快速查询。所以,对于数组我们最好应用在“索引有语义”的情况。我们知道我们要查的是什么。
但是并非所有的有语义的索引都适合用于数组!
例如身份证号33333333333332442444444222!
这个时候,也可以使用数组处理“索引没有语义”的情况。
下面制作属于我们自己的数组类:
1 package cn.zcb.demo01; 2 3 public class MyArray { 4 private int[] data; //数组 5 private int size; //数组实际大小, 6 //容量不用写,可以由data.length()得知 7 8 //满参构造函数 9 public MyArray(int capacity){ 10 this.data = new int[capacity]; 11 this.size = 0; //开始时 实际大小size =0 12 } 13 //空参构造器 14 public MyArray(){ 15 this(10); //如果是空参的话,调用有参构造器。默认是10 16 } 17 }
1 package cn.zcb.demo01; 2 public class MyArray { 3 private int[] data; //数组 4 private int size; //数组实际大小, 5 //容量不用写,可以由data.length()得知 6 7 //满参构造函数 8 public MyArray(int capacity){ 9 this.data = new int[capacity]; 10 this.size = 0; //开始时 实际大小size =0 11 } 12 //空参构造器 13 public MyArray(){ 14 this(10); //如果是空参的话,调用有参构造器。默认是10 15 } 16 17 //获取数组中的元素的个数 18 public int getSize(){ 19 return this.size; 20 } 21 22 //获取数组的容量 23 public int getCapacity(){ 24 return this.data.length; 25 } 26 27 //数组中是否为空 28 public boolean isEmpty(){ 29 return this.size ==0; 30 } 31 }
向我们自己的数组中添加元素(在数组末加):
1 package cn.zcb.demo01; 2 public class MyArray { 3 private int[] data; //数组 4 private int size; //数组实际大小, 5 //容量不用写,可以由data.length()得知 6 7 //满参构造函数 8 public MyArray(int capacity){ 9 this.data = new int[capacity]; 10 this.size = 0; //开始时 实际大小size =0 11 } 12 //空参构造器 13 public MyArray(){ 14 this(10); //如果是空参的话,调用有参构造器。默认是10 15 } 16 17 //获取数组中的元素的个数 18 public int getSize(){ 19 return this.size; 20 } 21 22 //获取数组的容量 23 public int getCapacity(){ 24 return this.data.length; 25 } 26 27 //数组中是否为空 28 public boolean isEmpty(){ 29 return this.size ==0; 30 } 31 32 //向 数组的末尾添加一个新元素 33 public void addLast(int num){ 34 if(size == this.data.length){ 35 //此时开辟的空间已经满了 36 //这里先抛出一个异常 37 throw new IllegalArgumentException("addLast failed.数组已经满了"); 38 } 39 this.data[size] = num; 40 size ++; //要维持数组 指针。 size 是当前数组中的个数。 41 } 42 43 44 45 }
向我们自己的数组中添加元素(在数组指定位置添加):
1 package cn.zcb.demo01; 2 public class MyArray { 3 private int[] data; //数组 4 private int size; //数组实际大小, 5 //容量不用写,可以由data.length()得知 6 7 //满参构造函数 8 public MyArray(int capacity){ 9 this.data = new int[capacity]; 10 this.size = 0; //开始时 实际大小size =0 11 } 12 //空参构造器 13 public MyArray(){ 14 this(10); //如果是空参的话,调用有参构造器。默认是10 15 } 16 17 //获取数组中的元素的个数 18 public int getSize(){ 19 return this.size; 20 } 21 22 //获取数组的容量 23 public int getCapacity(){ 24 return this.data.length; 25 } 26 27 //数组中是否为空 28 public boolean isEmpty(){ 29 return this.size ==0; 30 } 31 32 //向 数组的末尾添加一个新元素 33 public void addLast(int num){ 34 if(size == this.data.length){ 35 //此时开辟的空间已经满了 36 //这里先抛出一个异常 37 throw new IllegalArgumentException("addLast failed.数组已经满了"); 38 } 39 this.data[size] = num; 40 size ++; //要维持数组 指针。 size 是当前数组中的个数。 41 } 42 43 //向 指定数组位置添加一个新元素 44 public void insert(int idx,int num){ 45 if(this.size == this.data.length){ 46 throw new IllegalArgumentException("数组已经放满了"); 47 } 48 if(idx <0 || idx > this.size ) 49 throw new IllegalArgumentException("索引非法"); 50 51 for(int i=this.size-1;i >= idx;--i){ 52 this.data[i+1] = this.data[i]; 53 } 54 this.data[idx] = num; 55 //更新size 56 this.size ++; 57 } 58 }
1 package cn.zcb.demo01; 2 public class MyArray { 3 private int[] data; //数组 4 private int size; //数组实际大小, 5 //容量不用写,可以由data.length()得知 6 7 //满参构造函数 8 public MyArray(int capacity){ 9 this.data = new int[capacity]; 10 this.size = 0; //开始时 实际大小size =0 11 } 12 //空参构造器 13 public MyArray(){ 14 this(10); //如果是空参的话,调用有参构造器。默认是10 15 } 16 17 //获取数组中的元素的个数 18 public int getSize(){ 19 return this.size; 20 } 21 22 //获取数组的容量 23 public int getCapacity(){ 24 return this.data.length; 25 } 26 27 //数组中是否为空 28 public boolean isEmpty(){ 29 return this.size ==0; 30 } 31 32 //向 数组的末尾添加一个新元素 33 public void addLast(int num){ 34 /* 35 if(size == this.data.length){ 36 //此时开辟的空间已经满了 37 //这里先抛出一个异常 38 throw new IllegalArgumentException("addLast failed.数组已经满了"); 39 } 40 this.data[size] = num; 41 size ++; //要维持数组 指针。 size 是当前数组中的个数。 42 */ 43 //此时其实可以直接复用 insert ()函数 44 insert(this.size,num); 45 } 46 47 //向 指定数组位置添加一个新元素 48 public void insert(int idx,int num){ 49 if(this.size == this.data.length){ 50 throw new IllegalArgumentException("数组已经放满了"); 51 } 52 if(idx <0 || idx > this.size ) 53 throw new IllegalArgumentException("索引非法"); 54 55 for(int i=this.size-1;i >= idx;--i){ 56 this.data[i+1] = this.data[i]; 57 } 58 this.data[idx] = num; 59 //更新size 60 this.size ++; 61 } 62 }
而且,给数组头添加元素也可以直接复用 给指定位置添加元素的 函数!!!
1 package cn.zcb.demo01; 2 public class MyArray { 3 private int[] data; //数组 4 private int size; //数组实际大小, 5 //容量不用写,可以由data.length()得知 6 7 //满参构造函数 8 public MyArray(int capacity){ 9 this.data = new int[capacity]; 10 this.size = 0; //开始时 实际大小size =0 11 } 12 //空参构造器 13 public MyArray(){ 14 this(10); //如果是空参的话,调用有参构造器。默认是10 15 } 16 17 //获取数组中的元素的个数 18 public int getSize(){ 19 return this.size; 20 } 21 22 //获取数组的容量 23 public int getCapacity(){ 24 return this.data.length; 25 } 26 27 //数组中是否为空 28 public boolean isEmpty(){ 29 return this.size ==0; 30 } 31 32 //向 数组的末尾添加一个新元素 33 public void addLast(int num){ 34 /* 35 if(size == this.data.length){ 36 //此时开辟的空间已经满了 37 //这里先抛出一个异常 38 throw new IllegalArgumentException("addLast failed.数组已经满了"); 39 } 40 this.data[size] = num; 41 size ++; //要维持数组 指针。 size 是当前数组中的个数。 42 */ 43 //此时其实可以直接复用 insert ()函数 44 insert(this.size,num); 45 } 46 // 向数组头添加元素 (直接复用 向指定位置添加元素的函数 ) 47 public void addFirst(int num){ 48 insert(0,num); 49 } 50 51 52 53 //向 指定数组位置添加一个新元素 54 public void insert(int idx,int num){ 55 if(this.size == this.data.length){ 56 throw new IllegalArgumentException("数组已经放满了"); 57 } 58 if(idx <0 || idx > this.size ) 59 throw new IllegalArgumentException("索引非法"); 60 61 for(int i=this.size-1;i >= idx;--i){ 62 this.data[i+1] = this.data[i]; 63 } 64 this.data[idx] = num; 65 //更新size 66 this.size ++; 67 } 68 }
在我们自己的数组中查询和修改操作:
1 package cn.zcb.demo01; 2 public class MyArray { 3 private int[] data; //数组 4 private int size; //数组实际大小, 5 //容量不用写,可以由data.length()得知 6 7 //满参构造函数 8 public MyArray(int capacity){ 9 this.data = new int[capacity]; 10 this.size = 0; //开始时 实际大小size =0 11 } 12 //空参构造器 13 public MyArray(){ 14 this(10); //如果是空参的话,调用有参构造器。默认是10 15 } 16 17 //获取数组中的元素的个数 18 public int getSize(){ 19 return this.size; 20 } 21 22 //获取数组的容量 23 public int getCapacity(){ 24 return this.data.length; 25 } 26 27 //数组中是否为空 28 public boolean isEmpty(){ 29 return this.size ==0; 30 } 31 /////////////////////增//////////////////////////// 32 //向 数组的末尾添加一个新元素 33 public void addLast(int num){ 34 /* 35 if(size == this.data.length){ 36 //此时开辟的空间已经满了 37 //这里先抛出一个异常 38 throw new IllegalArgumentException("addLast failed.数组已经满了"); 39 } 40 this.data[size] = num; 41 size ++; //要维持数组 指针。 size 是当前数组中的个数。 42 */ 43 //此时其实可以直接复用 insert ()函数 44 insert(this.size,num); 45 } 46 // 向数组头添加元素 (直接复用 向指定位置添加元素的函数 ) 47 public void addFirst(int num){ 48 insert(0,num); 49 } 50 51 //向 指定数组位置添加一个新元素 52 public void insert(int idx,int num){ 53 if(this.size == this.data.length){ 54 throw new IllegalArgumentException("数组已经放满了"); 55 } 56 if(idx <0 || idx > this.size ) 57 throw new IllegalArgumentException("索引非法"); 58 59 for(int i=this.size-1;i >= idx;--i){ 60 this.data[i+1] = this.data[i]; 61 } 62 this.data[idx] = num; 63 //更新size 64 this.size ++; 65 } 66 //////////////////////改/////////////////////////////////////// 67 //修改索引为 idx 的元素为 num 68 public void setByIndex(int idx,int num){ 69 if(idx <0 || idx > this.size) 70 throw new IllegalArgumentException("idx 错误!"); 71 72 this.data[idx] = num; 73 } 74 75 76 //////////////////////查/////////////////////////////////////// 77 //获取整个数组的概括 78 //重写 toString() 当打印arr 时候,就会调用该方法! 79 @Override 80 public String toString(){ 81 StringBuilder res = new StringBuilder(); 82 res.append(String.format("Array:size = %d,capacity = %d ",this.size,this.data.length)); 83 res.append('['); 84 for (int i =0 ;i<this.size;i++){ 85 res.append(data[i]); 86 if(i != this.size -1) 87 res.append(", "); 88 } 89 res.append(']'); 90 return res.toString(); 91 } 92 //获取idx 索引位置的 元素 93 public int getByIndex(int idx){ 94 if (idx < 0 || idx >this.size) { 95 throw new IllegalArgumentException("索引传入错误!") 96 } 97 return this.data[idx]; 98 } 99 100 101 102 }
在我们自己的数组中 包含,搜索,和删除 操作:
1 package cn.zcb.demo01; 2 public class MyArray { 3 private int[] data; //数组 4 private int size; //数组实际大小, 5 //容量不用写,可以由data.length()得知 6 7 //满参构造函数 8 public MyArray(int capacity){ 9 this.data = new int[capacity]; 10 this.size = 0; //开始时 实际大小size =0 11 } 12 //空参构造器 13 public MyArray(){ 14 this(10); //如果是空参的话,调用有参构造器。默认是10 15 } 16 17 //获取数组中的元素的个数 18 public int getSize(){ 19 return this.size; 20 } 21 22 //获取数组的容量 23 public int getCapacity(){ 24 return this.data.length; 25 } 26 27 //数组中是否为空 28 public boolean isEmpty(){ 29 return this.size ==0; 30 } 31 /////////////////////增//////////////////////////// 32 //向 数组的末尾添加一个新元素 33 public void addLast(int num){ 34 /* 35 if(size == this.data.length){ 36 //此时开辟的空间已经满了 37 //这里先抛出一个异常 38 throw new IllegalArgumentException("addLast failed.数组已经满了"); 39 } 40 this.data[size] = num; 41 size ++; //要维持数组 指针。 size 是当前数组中的个数。 42 */ 43 //此时其实可以直接复用 insert ()函数 44 insert(this.size,num); 45 } 46 // 向数组头添加元素 (直接复用 向指定位置添加元素的函数 ) 47 public void addFirst(int num){ 48 insert(0,num); 49 } 50 51 //向 指定数组位置添加一个新元素 52 public void insert(int idx,int num){ 53 if(this.size == this.data.length){ 54 throw new IllegalArgumentException("数组已经放满了"); 55 } 56 if(idx <0 || idx > this.size ) 57 throw new IllegalArgumentException("索引非法"); 58 59 for(int i=this.size-1;i >= idx;--i){ 60 this.data[i+1] = this.data[i]; 61 } 62 this.data[idx] = num; 63 //更新size 64 this.size ++; 65 } 66 67 //////////////////////删/////////////////////////////////////// 68 //删除第一个 69 public int removeFirst(){ 70 return remove(0); //删除并将之返回出去 71 } 72 73 //删除最后一个 74 public int removeLast(){ 75 return remove(this.size-1); 76 } 77 78 //删除指定位置 的元素 79 public int remove(int idx){ //返回 删除的元素 80 if(idx < 0 || idx > this.size -1) 81 throw new IllegalArgumentException("idx错误!"); 82 int temp = this.data[idx]; 83 for (int i =idx;i<this.size - 1;i++){ 84 this.data[i] = this.data[i+1]; 85 } 86 87 this.size -- ; 88 return temp; 89 } 90 91 //删除 某个元素 不根据其位置 //它之所以返回void 是因为调用着已经知道的element ,所以没必要再返回出来了。 92 public void removeElement(int element){ 93 //首先要 寻找是否有 element 94 int idx = find(element); 95 if(idx == -1){ 96 throw new IllegalArgumentException("没有该元素"); 97 }else{ 98 remove(idx); 99 } 100 } 101 102 //////////////////////改/////////////////////////////////////// 103 //修改索引为 idx 的元素为 num 104 public void setByIndex(int idx,int num){ 105 if(idx <0 || idx > this.size) 106 throw new IllegalArgumentException("idx 错误!"); 107 108 this.data[idx] = num; 109 } 110 111 112 //////////////////////查/////////////////////////////////////// 113 //获取整个数组的概括 114 //重写 toString() 当打印arr 时候,就会调用该方法! 115 @Override 116 public String toString(){ 117 StringBuilder res = new StringBuilder(); 118 res.append(String.format("Array:size = %d,capacity = %d ",this.size,this.data.length)); 119 res.append('['); 120 for (int i =0 ;i<this.size;i++){ 121 res.append(data[i]); 122 if(i != this.size -1) 123 res.append(", "); 124 } 125 res.append(']'); 126 return res.toString(); 127 } 128 //获取idx 索引位置的 元素 129 public int getByIndex(int idx){ 130 if (idx < 0 || idx >this.size) { 131 throw new IllegalArgumentException("索引传入错误!"); 132 } 133 return this.data[idx]; 134 } 135 136 //////////////////////包含/////////////////////////////////////// 137 public boolean contains(int num){ 138 for (int i=0;i<this.size;i++){ 139 if(this.data[i] == num){ 140 return true; 141 } 142 } 143 return false; 144 } 145 //////////////////////寻找//////////////////////////////////// 146 public int find(int num){ 147 //寻找数组中是否有 num ,如果有返回其索引,反之 返回-1 148 for (int i =0;i<this.size;i++){ 149 if(this.data[i] == num){ 150 return i; 151 } 152 } 153 return -1; 154 } 155 156 157 }
到目前为止,我们自己的数组类已经初具雏形了!
调试代码:
1 package cn.zcb.demo01; 2 3 public class Test { 4 public static void main(String[] args) { 5 MyArray arr = new MyArray(10); 6 7 //数组的容量 8 System.out.println(arr.getCapacity()); 9 10 //目前数组的大小 11 System.out.println(arr.getSize()); 12 13 //在数组末尾加一个数 14 arr.addLast(18); 15 16 //在 0 索引加入 7 17 arr.insert(0,7); 18 19 System.out.println(arr.getSize()); 20 21 //查看数组内容 会调用重写之后的toString() 22 System.out.println(arr); 23 24 //在数组的头部 加-10 25 arr.addFirst(-10); 26 System.out.println(arr); 27 28 System.out.println(arr.contains(7)); 29 System.out.println(arr.contains(101)); 30 31 //删除指定位置的元素 32 int res = arr.remove(0); //删除索引为0 的元素 33 System.out.println(res); 34 System.out.println(arr); //再次打印 arr 35 36 37 } 38 }
改善我们自己的数组之---使用泛型:
目前我们自己写的数组类最大的问题就是它只能存放int 数据类型,这肯定是不行的。
1 package cn.zcb.demo01; 2 3 public class MyArray<T> { 4 private T[] data; //数组 5 private int size; //数组实际大小, 6 //容量不用写,可以由data.length()得知 7 8 //满参构造函数 9 public MyArray(int capacity){ 10 // this.data = new int[capacity]; 11 // this.data = new T[capacity]; //不支持这样 12 this.data = (T[]) new Object[capacity]; //先new 出个 Object[] 类的,然后转为 T[] 13 this.size = 0; //开始时 实际大小size =0 14 } 15 //空参构造器 16 public MyArray(){ 17 this(10); //如果是空参的话,调用有参构造器。默认是10 18 } 19 20 //获取数组中的元素的个数 21 public int getSize(){ 22 return this.size; 23 } 24 25 //获取数组的容量 26 public int getCapacity(){ 27 return this.data.length; 28 } 29 30 //数组中是否为空 31 public boolean isEmpty(){ 32 return this.size ==0; 33 } 34 /////////////////////增//////////////////////////// 35 //向 数组的末尾添加一个新元素 36 public void addLast(T num){ 37 /* 38 if(size == this.data.length){ 39 //此时开辟的空间已经满了 40 //这里先抛出一个异常 41 throw new IllegalArgumentException("addLast failed.数组已经满了"); 42 } 43 this.data[size] = num; 44 size ++; //要维持数组 指针。 size 是当前数组中的个数。 45 */ 46 //此时其实可以直接复用 insert ()函数 47 insert(this.size,num); 48 } 49 // 向数组头添加元素 (直接复用 向指定位置添加元素的函数 ) 50 public void addFirst(T num){ 51 insert(0,num); 52 } 53 54 //向 指定数组位置添加一个新元素 55 public void insert(int idx,T num){ 56 if(this.size == this.data.length){ 57 throw new IllegalArgumentException("数组已经放满了"); 58 } 59 if(idx <0 || idx > this.size ) 60 throw new IllegalArgumentException("索引非法"); 61 62 for(int i=this.size-1;i >= idx;--i){ 63 this.data[i+1] = this.data[i]; 64 } 65 this.data[idx] = num; 66 //更新size 67 this.size ++; 68 } 69 70 //////////////////////删/////////////////////////////////////// 71 //删除第一个 72 public T removeFirst(){ 73 return remove(0); //删除并将之返回出去 74 } 75 76 //删除最后一个 77 public T removeLast(){ 78 return remove(this.size-1); 79 } 80 81 //删除指定位置 的元素 82 public T remove(int idx){ //返回 删除的元素 83 if(idx < 0 || idx > this.size -1) 84 throw new IllegalArgumentException("idx错误!"); 85 T temp = this.data[idx]; 86 for (int i =idx;i<this.size - 1;i++){ 87 this.data[i] = this.data[i+1]; 88 } 89 90 this.size -- ; 91 return temp; 92 } 93 94 //删除 某个元素 不根据其位置 //它之所以返回void 是因为调用着已经知道的element ,所以没必要再返回出来了。 95 public void removeElement(T element){ 96 //首先要 寻找是否有 element 97 int idx = find(element); 98 if(idx == -1){ 99 throw new IllegalArgumentException("没有该元素"); 100 }else{ 101 remove(idx); 102 } 103 } 104 105 //////////////////////改/////////////////////////////////////// 106 //修改索引为 idx 的元素为 num 107 public void setByIndex(int idx,T num){ 108 if(idx <0 || idx > this.size) 109 throw new IllegalArgumentException("idx 错误!"); 110 111 this.data[idx] = num; 112 } 113 114 115 //////////////////////查/////////////////////////////////////// 116 //获取整个数组的概括 117 //重写 toString() 当打印arr 时候,就会调用该方法! 118 @Override 119 public String toString(){ 120 StringBuilder res = new StringBuilder(); 121 res.append(String.format("Array:size = %d,capacity = %d ",this.size,this.data.length)); 122 res.append('['); 123 for (int i =0 ;i<this.size;i++){ 124 res.append(data[i]); 125 if(i != this.size -1) 126 res.append(", "); 127 } 128 res.append(']'); 129 return res.toString(); 130 } 131 //获取idx 索引位置的 元素 132 public T getByIndex(int idx){ 133 if (idx < 0 || idx >this.size) { 134 throw new IllegalArgumentException("索引传入错误!"); 135 } 136 return this.data[idx]; 137 } 138 139 //////////////////////包含/////////////////////////////////////// 140 public boolean contains(T num){ 141 for (int i=0;i<this.size;i++){ 142 if(this.data[i] == num){ 143 return true; 144 } 145 } 146 return false; 147 } 148 //////////////////////寻找//////////////////////////////////// 149 public int find(T num){ 150 //寻找数组中是否有 num ,如果有返回其索引,反之 返回-1 151 for (int i =0;i<this.size;i++){ 152 if(this.data[i] == num){ 153 return i; 154 } 155 } 156 return -1; 157 } 158 159 }
1 package cn.zcb.demo01; 2 3 public class Person { 4 5 private String name; 6 private int age; 7 8 public Person(String name,int age){ 9 this.name = name; 10 this.age = age; 11 } 12 public Person(){ 13 this("张三",18); //默认赋值 张三 18 14 } 15 16 17 }
1 package cn.zcb.demo01; 2 3 public class Test { 4 5 6 public static void main(String[] args) { 7 MyArray<Integer> arrInt = new MyArray<Integer>(11); 8 arrInt.addLast(10); 9 arrInt.addLast(11); 10 arrInt.addLast(12); 11 System.out.println(arrInt); 12 13 MyArray<Character> arrChar = new MyArray<Character>(11); 14 arrChar.addLast('1'); 15 arrChar.addLast('a'); 16 arrChar.addLast('b'); 17 arrChar.addLast('c'); 18 System.out.println(arrChar); 19 20 //自定义类型 Person 21 Person p1 = new Person("tom",18); 22 Person p2 = new Person("egon",20); 23 Person p3 = new Person("alex",30); 24 25 MyArray<Person> arrPerson = new MyArray<>(); // <> 中的类型是可以省略的。 26 arrPerson.addLast(p1); 27 arrPerson.addLast(p2); 28 arrPerson.addLast(p3); 29 System.out.println(arrPerson); 30 } 31 } 32 /* 输出结果 33 Array:size = 3,capacity = 11 34 [10, 11, 12] 35 Array:size = 4,capacity = 11 36 [1, a, b, c] 37 Array:size = 3,capacity = 10 38 [cn.zcb.demo01.Person@17a7cec2, cn.zcb.demo01.Person@65b3120a, cn.zcb.demo01.Person@6f539caf] 39 * */
改善我们自己的数组之---动态数组:
此时不够用了,
以上整个过程是封装在一个函数中。
原本的四个的数组,由于没有人指向它了,所以Java的垃圾回收机制会将它回收。
上面其实有个缺点是:如果一个数组太大的话,每一个都遍历复制到一个新的地方,这确实是在性能上有所缺陷。这个问题将在下面讨论。
而且,删除的时候,也是用的这个思路。这样就可以实现动态数组了。
1 package cn.zcb.demo01; 2 3 public class MyArray<T> { 4 private T[] data; //数组 5 private int size; //数组实际大小, 6 //容量不用写,可以由data.length()得知 7 8 //满参构造函数 9 public MyArray(int capacity){ 10 // this.data = new int[capacity]; 11 // this.data = new T[capacity]; //不支持这样 12 this.data = (T[]) new Object[capacity]; //先new 出个 Object[] 类的,然后转为 T[] 13 this.size = 0; //开始时 实际大小size =0 14 } 15 //空参构造器 16 public MyArray(){ 17 this(10); //如果是空参的话,调用有参构造器。默认是10 18 } 19 20 //获取数组中的元素的个数 21 public int getSize(){ 22 return this.size; 23 } 24 25 //获取数组的容量 26 public int getCapacity(){ 27 return this.data.length; 28 } 29 30 //数组中是否为空 31 public boolean isEmpty(){ 32 return this.size ==0; 33 } 34 /////////////////////增//////////////////////////// 35 //向 数组的末尾添加一个新元素 36 public void addLast(T num){ 37 /* 38 if(size == this.data.length){ 39 //此时开辟的空间已经满了 40 //这里先抛出一个异常 41 throw new IllegalArgumentException("addLast failed.数组已经满了"); 42 } 43 this.data[size] = num; 44 size ++; //要维持数组 指针。 size 是当前数组中的个数。 45 */ 46 //此时其实可以直接复用 insert ()函数 47 insert(this.size,num); 48 } 49 // 向数组头添加元素 (直接复用 向指定位置添加元素的函数 ) 50 public void addFirst(T num){ 51 insert(0,num); 52 } 53 54 //向 指定数组位置添加一个新元素 55 public void insert(int idx,T num){ 56 if(this.size == this.data.length){ 57 // throw new IllegalArgumentException("数组已经放满了"); 58 /*此时对其扩容*/ 59 resize(2*this.data.length); 60 61 62 } 63 if(idx <0 || idx > this.size ) 64 throw new IllegalArgumentException("索引非法"); 65 66 for(int i=this.size-1;i >= idx;--i){ 67 this.data[i+1] = this.data[i]; 68 } 69 this.data[idx] = num; 70 //更新size 71 this.size ++; 72 } 73 74 //////////////////////删/////////////////////////////////////// 75 //删除第一个 76 public T removeFirst(){ 77 return remove(0); //删除并将之返回出去 78 } 79 80 //删除最后一个 81 public T removeLast(){ 82 return remove(this.size-1); 83 } 84 85 //删除指定位置 的元素 86 public T remove(int idx){ //返回 删除的元素 87 if(idx < 0 || idx > this.size -1) 88 throw new IllegalArgumentException("idx错误!"); 89 T temp = this.data[idx]; 90 for (int i =idx;i<this.size - 1;i++){ 91 this.data[i] = this.data[i+1]; 92 } 93 this.size -- ; 94 95 //动态减少 当数组中的元素少于空间的一半的时候,就缩减空间。 96 if(this.size == this.data.length / 2){ 97 System.out.println("size = "+this.size); 98 99 resize(this.data.length/2); 100 } 101 return temp; 102 } 103 104 //删除 某个元素 不根据其位置 //它之所以返回void 是因为调用着已经知道的element ,所以没必要再返回出来了。 105 public void removeElement(T element){ 106 //首先要 寻找是否有 element 107 int idx = find(element); 108 if(idx == -1){ 109 throw new IllegalArgumentException("没有该元素"); 110 }else{ 111 remove(idx); 112 } 113 } 114 115 //////////////////////改/////////////////////////////////////// 116 //修改索引为 idx 的元素为 num 117 public void setByIndex(int idx,T num){ 118 if(idx <0 || idx > this.size) 119 throw new IllegalArgumentException("idx 错误!"); 120 121 this.data[idx] = num; 122 } 123 124 125 //////////////////////查/////////////////////////////////////// 126 //获取整个数组的概括 127 //重写 toString() 当打印arr 时候,就会调用该方法! 128 @Override 129 public String toString(){ 130 StringBuilder res = new StringBuilder(); 131 res.append(String.format("Array:size = %d,capacity = %d ",this.size,this.data.length)); 132 res.append('['); 133 for (int i =0 ;i<this.size;i++){ 134 res.append(data[i]); 135 if(i != this.size -1) 136 res.append(", "); 137 } 138 res.append(']'); 139 return res.toString(); 140 } 141 //获取idx 索引位置的 元素 142 public T getByIndex(int idx){ 143 if (idx < 0 || idx >this.size) { 144 throw new IllegalArgumentException("索引传入错误!"); 145 } 146 return this.data[idx]; 147 } 148 149 //////////////////////包含/////////////////////////////////////// 150 public boolean contains(T num){ 151 for (int i=0;i<this.size;i++){ 152 if(this.data[i] == num){ 153 return true; 154 } 155 } 156 return false; 157 } 158 //////////////////////寻找//////////////////////////////////// 159 public int find(T num){ 160 //寻找数组中是否有 num ,如果有返回其索引,反之 返回-1 161 for (int i =0;i<this.size;i++){ 162 if(this.data[i] == num){ 163 return i; 164 } 165 } 166 return -1; 167 } 168 //////////////////////实现动态数组 resize()//////////////////////////////////// 169 private void resize(int new_capacity){ //之所以 用 private 是不让用户调用, 数组如果空间不足会自己扩展的。 170 T[] new_data =(T[]) new Object[new_capacity]; 171 172 for (int i =0;i<this.size;i++){ 173 new_data[i] = this.data[i]; 174 } 175 // 176 this.data = new_data; //此时新数组的size 不变 , 新数组的length 也不用变 177 } 178 }
1 package cn.zcb.demo01; 2 /* 动态增加测试 3 public class Test { 4 5 6 public static void main(String[] args) { 7 MyArray<Integer> arrInt = new MyArray<Integer>(4); 8 arrInt.addLast(10); 9 arrInt.addLast(11); 10 arrInt.addLast(12); 11 arrInt.addLast(13); 12 System.out.println(arrInt); 13 arrInt.addLast(14); 14 System.out.println(arrInt); 15 } 16 } 17 输出结果 18 Array:size = 4,capacity = 4 19 [10, 11, 12, 13] 20 Array:size = 5,capacity = 8 21 [10, 11, 12, 13, 14] 22 * */ 23 24 //动态减少测试 n =4 时的减少 25 /* 26 public class Test { 27 public static void main(String[] args) { 28 MyArray<Integer> arrInt = new MyArray<Integer>(4); 29 arrInt.addLast(10); 30 arrInt.addLast(11); 31 arrInt.addLast(12); 32 arrInt.addLast(13); 33 System.out.println(arrInt); 34 arrInt.remove(0); //删除 35 arrInt.remove(0); 36 System.out.println(arrInt); 37 } 38 } 39 40 输出结果 41 Array:size = 4,capacity = 4 42 [10, 11, 12, 13] 43 size = 2 44 Array:size = 2,capacity = 2 45 [12, 13] 46 * */ 47 48 49 50 //动态减少测试 n =5 时的减少 51 52 public class Test { 53 public static void main(String[] args) { 54 MyArray<Integer> arrInt = new MyArray<Integer>(5); 55 arrInt.addLast(10); 56 arrInt.addLast(11); 57 arrInt.addLast(12); 58 arrInt.addLast(13); 59 arrInt.addLast(14); 60 System.out.println(arrInt); 61 arrInt.remove(0); //删除 62 arrInt.remove(0); 63 arrInt.remove(0); 64 System.out.println(arrInt); 65 } 66 } 67 /* 输出结果 68 Array:size = 5,capacity = 5 69 [10, 11, 12, 13, 14] 70 size = 2 71 Array:size = 2,capacity = 2 72 [13, 14] 73 * */
改善我们自己的数组之---简单的时间复杂度分析:
其实c1 和 c2 是很难精确得知的,它在不同语言不同平台都可能有差别!
时间复杂度为O(n) 的 一定比 时间复杂度为O(n^2) 的优吗?
不一定,例如上述的第二个和第三个,当n =10 时,第二个的T= 30000 第三个的T= 100
因此,针对任意一个输入,并不是O(n) 的算法,一定优于O(n^2) 的(此时O(n) 的系数起到大的作用!) 。
但,大O ,描述的其实是当n 趋近于无穷的时候的情况。
resize 的时间复杂度分析:
虽然上述分析没有错误,但是,我们忽略了一个问题,就是并不是每次我们都需要进行resize操作。
可以如下分析:
但是同时进行addLast 和 removeLast 时候就出问题了:
这个问题叫做复杂度震荡!
是数组长度的1/4 的时候,再缩容,缩小1/2 。
这样就可以有效的防止了 复杂度震荡的问题了。
所以,在算法中有时“懒”一点反而可以提高代码的整体性能!!!
改善之后的代码:主要在remove()中:
1 package cn.zcb.demo01; 2 3 public class MyArray<T> { 4 private T[] data; //数组 5 private int size; //数组实际大小, 6 //容量不用写,可以由data.length()得知 7 8 //满参构造函数 9 public MyArray(int capacity){ 10 // this.data = new int[capacity]; 11 // this.data = new T[capacity]; //不支持这样 12 this.data = (T[]) new Object[capacity]; //先new 出个 Object[] 类的,然后转为 T[] 13 this.size = 0; //开始时 实际大小size =0 14 } 15 //空参构造器 16 public MyArray(){ 17 this(10); //如果是空参的话,调用有参构造器。默认是10 18 } 19 20 //获取数组中的元素的个数 21 public int getSize(){ 22 return this.size; 23 } 24 25 //获取数组的容量 26 public int getCapacity(){ 27 return this.data.length; 28 } 29 30 //数组中是否为空 31 public boolean isEmpty(){ 32 return this.size ==0; 33 } 34 /////////////////////增//////////////////////////// 35 //向 数组的末尾添加一个新元素 36 public void addLast(T num){ 37 /* 38 if(size == this.data.length){ 39 //此时开辟的空间已经满了 40 //这里先抛出一个异常 41 throw new IllegalArgumentException("addLast failed.数组已经满了"); 42 } 43 this.data[size] = num; 44 size ++; //要维持数组 指针。 size 是当前数组中的个数。 45 */ 46 //此时其实可以直接复用 insert ()函数 47 insert(this.size,num); 48 } 49 // 向数组头添加元素 (直接复用 向指定位置添加元素的函数 ) 50 public void addFirst(T num){ 51 insert(0,num); 52 } 53 54 //向 指定数组位置添加一个新元素 55 public void insert(int idx,T num){ 56 if(this.size == this.data.length){ 57 // throw new IllegalArgumentException("数组已经放满了"); 58 /*此时对其扩容*/ 59 resize(2*this.data.length); 60 61 62 } 63 if(idx <0 || idx > this.size ) 64 throw new IllegalArgumentException("索引非法"); 65 66 for(int i=this.size-1;i >= idx;--i){ 67 this.data[i+1] = this.data[i]; 68 } 69 this.data[idx] = num; 70 //更新size 71 this.size ++; 72 } 73 74 //////////////////////删/////////////////////////////////////// 75 //删除第一个 76 public T removeFirst(){ 77 return remove(0); //删除并将之返回出去 78 } 79 80 //删除最后一个 81 public T removeLast(){ 82 return remove(this.size-1); 83 } 84 85 //删除指定位置 的元素 86 public T remove(int idx){ //返回 删除的元素 87 if(idx < 0 || idx > this.size -1) 88 throw new IllegalArgumentException("idx错误!"); 89 T temp = this.data[idx]; 90 for (int i =idx;i<this.size - 1;i++){ 91 this.data[i] = this.data[i+1]; 92 } 93 this.size -- ; 94 95 //动态减少 当数组中的元素少于空间的四分之一的时候,缩减空间为原来的一半(这样可以有效的防止 复杂度震荡问题)。 96 if(this.size == this.data.length / 4 && this.data.length/2 != 0 ){ //第二个条件是防止 length =1 的时候 97 resize(this.data.length/2); 98 } 99 return temp; 100 } 101 102 //删除 某个元素 不根据其位置 //它之所以返回void 是因为调用着已经知道的element ,所以没必要再返回出来了。 103 public void removeElement(T element){ 104 //首先要 寻找是否有 element 105 int idx = find(element); 106 if(idx == -1){ 107 throw new IllegalArgumentException("没有该元素"); 108 }else{ 109 remove(idx); 110 } 111 } 112 113 //////////////////////改/////////////////////////////////////// 114 //修改索引为 idx 的元素为 num 115 public void setByIndex(int idx,T num){ 116 if(idx <0 || idx > this.size) 117 throw new IllegalArgumentException("idx 错误!"); 118 119 this.data[idx] = num; 120 } 121 122 123 //////////////////////查/////////////////////////////////////// 124 //获取整个数组的概括 125 //重写 toString() 当打印arr 时候,就会调用该方法! 126 @Override 127 public String toString(){ 128 StringBuilder res = new StringBuilder(); 129 res.append(String.format("Array:size = %d,capacity = %d ",this.size,this.data.length)); 130 res.append('['); 131 for (int i =0 ;i<this.size;i++){ 132 res.append(data[i]); 133 if(i != this.size -1) 134 res.append(", "); 135 } 136 res.append(']'); 137 return res.toString(); 138 } 139 //获取idx 索引位置的 元素 140 public T getByIndex(int idx){ 141 if (idx < 0 || idx >this.size) { 142 throw new IllegalArgumentException("索引传入错误!"); 143 } 144 return this.data[idx]; 145 } 146 147 //////////////////////包含/////////////////////////////////////// 148 public boolean contains(T num){ 149 for (int i=0;i<this.size;i++){ 150 if(this.data[i] == num){ 151 return true; 152 } 153 } 154 return false; 155 } 156 //////////////////////寻找//////////////////////////////////// 157 public int find(T num){ 158 //寻找数组中是否有 num ,如果有返回其索引,反之 返回-1 159 for (int i =0;i<this.size;i++){ 160 if(this.data[i] == num){ 161 return i; 162 } 163 } 164 return -1; 165 } 166 //////////////////////实现动态数组 resize()//////////////////////////////////// 167 private void resize(int new_capacity){ //之所以 用 private 是不让用户调用, 数组如果空间不足会自己扩展的。 168 T[] new_data =(T[]) new Object[new_capacity]; 169 170 for (int i =0;i<this.size;i++){ 171 new_data[i] = this.data[i]; 172 } 173 // 174 this.data = new_data; //此时新数组的size 不变 , 新数组的length 也不用变 175 } 176 }