我看了看慕课网上的视频,对于视频的东西进行一个整理,最开始我们先从最基础的数组开始.
我们知道Java中的数组是只能存储基本类型,我们在这里可以制作一个可以存储对象的数组,其实也就是集合,我们就是自己去写一个集合。
首先,我们要定义我们的集合的底层,我们这个集合的底层就是数组,为了规范我们将底层和数组的大小里面存储的书=数的数量设置为private形式,用户不需要去能够使用到底层的知识,他们也不需要去更改,他们使用的时候只需要去调用我们写好的代码就可以了。
private E[]data;
private int size;
在这里我们需要强调两个概念,size和capacity
size是指的是数组中元素的个数而capacity指的是数组的容量,这个需要好好地区分开,我们在数组里面存储元素,数组不一定会被装满,而capacity一旦被装满了我们就需要去扩容了,这个问题我们稍后再说
然后就是构造方法,我们作为一个需要有他的构造方法,不仅仅是一个无参构造就可以完成了,还可以去写有参数的构造,(就像HashMap他其实是有四个构造函数的,具体的源码可以看我的博客,我稍微的介绍了HasMap中的一些知识)可以去穿指定大小的数组,到后面数组的扩容一定会浪费资源,如果我们最开始就完成了数组的建立,不用扩容我们就会很节省资源。
//有参构造
public Array(int capacity) {
data=(E[])new Object[capacity];
size=0;
}
//无参构造,默认size为0
public Array() {
this(10);
}
后面是一些基本的方法,例如说获得元素的个数,获得数组的容量,判断数组的是否为空,是否包含某个元素,寻找某个元素的位置,打印对象。
/**
*获得元素的个数
* @return
*/
public int getSize() {
return size;
}
/**
* 获得数组的容量
* @return
*/
public int getCapacity() {
return data.length;
}
/**
* 判断数组是否为空
* @return
*/
public boolean isEmpty() {
return size==0;
}
/**
* 如果有则返回true
* 没有则返回false
* @param e
* @return
*/
public boolean contains(E e) {
for (int i = 0; i < size; i++) {
if(data[i].equals(e)) {
return true;
}
}
return false;
}
/**
* 判断元素在里面是第几个如果有则显示位置,没有则返回-1
* @param e
* @return
*/
public int find(E e) {
for (int i = 0; i < size; i++) {
if(data[i].equals(e)) {
return i;
}
}
return -1;
}
@Override
public String toString() {
StringBuilder res=new StringBuilder();
res.append(String.format("Array: size= %d , capacity= %d\n",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();
}
数组的增删改查,这个我们手写的关键
数组的增加
首先是在某个位置增加某个元素,逻辑是先判断size的合法性,如果是大小等于数组的大小,则扩容,不合法报错,合法的话遍历找到位置,在制定位置往后移,指定位置插入元素,size++。
/**
* 在index的位置上添加一个元素e
* @param index
* @param e
*/
public void add(int index ,E e) {
if(size==data.length) {
resize(2*data.length);
}
if(index<0 ||index>size) {
throw new IllegalArgumentException(">0");
}
for (int i = size-1; i >=index; i--) {
data[i+1]=data[i];
}
data[index]=e;
size++;
}
可以在后面添加一些添加的便捷方法
/**
*加在最后
* @param e
*/
public void addList(E e) {
// if(size==data.length) {
// throw new IllegalArgumentException("已经满了");
// }
// data[size]=e;
// size++;
add(size, e);
}
/**
* 添加都头部位置
* @param e
*/
public void addfirst(E e) {
add(0, e);
}
数组的获取和修改
数组获取的逻辑判断给定的个数是否在合法的范围,加上便捷的方法。
/**
* 拿到第n个元素
* @param index
* @return
*/
public E get(int index) {
if(index<0||index>=size) {
throw new IllegalArgumentException("index要大于零");
}
return data[index];
}
public E getFirst() {
return get(0);
}
public E getLast() {
return get(size-1);
}
数组中指定位置的修改
public void set(int index,E e) {
if(index<0||index>=size) {
throw new IllegalArgumentException("index要大于零");
}
data[index]=e;
}
数组中数据的删除
数组中数据移除的逻辑,因为是数组如果一个元素被移除,则元素后面元素要向前移一个,size–.
/**
* 实现原理找到index的位置
* 然后将后一个一个一个向前一个替换,不暴露size个位置那样的话不需要去处理最后一个元素
* 因为访问不到
* 最开始的时候因为是基本类型但是以为里面是泛型所以size指向的其实是引用,所以要为了节省空间将其删除
* @return
*/
public E remove(int index) {
if(index<0||index>=size) {
throw new IllegalArgumentException("index要大于零");
}
E num=data[index];
for (int i = index+1; i < size; i++) {
data[i-1]=data[i];
}
size--;
data[size]=null;
//loitering objects!=memory leak
//闲散游散元素不动于内存泄漏,只是为了优化而优化
//缩减的时候要注意后面吗还有没有元素,如果有元素就不能就行缩减
if(size==data.length/4&&data.length/2!=0) {
//记上了&&为了避免空
resize(data.length/2);
}
return num;
}
/**
*移除最前
* @return
*/
public E removeFirst() {
// TODO Auto-generated method stub
return remove(0);
}
public E removeLast() {
// TODO Auto-generated method stub
return remove(size-1);
}
/**
*单纯只是为了删除其中的一个元素,没有考虑删除多个,也没有考虑删除以后的回复,单独就是一个删除操作
*本身的想法也只是为了删除其中的一个元素而已
*/
public void removeElement(E e) {
int find = find(e);
if(find!=-1) {
remove(find);
}
}
数组的扩容
数组的扩容这就牵扯到一个是时间复杂度的问题了,假如说数组的数量为10,超过10这个数字我们就要开始扩容,如果刚刚到10就开始扩容那么,万一一个数据就是在10的时候进行插入,插入以后到达临界点,继续插入下一条数据时,数组扩容原来的两倍,这时因为扩容操作空间复杂度是O(n),然后如果下一个操作是删除连续两次,数组进行缩容,数组变小了,然后还是一次缩容,空间复杂度是O(n),这个操作因为一步就要进行空间复杂度很大的操作,所以我们采用集合里面的做法,使用加载因子,当数组到达数组容量的时候我们再开始扩容,当数组的大小到达数组的大小的四分之一的时候才会开始缩容,这样的话我们就可以降低我们的空间复杂度,具体的例子就像是下面的
/**
* 实现原理找到index的位置
* 然后将后一个一个一个向前一个替换,不暴露size个位置那样的话不需要去处理最后一个元素
* 因为访问不到
* 最开始的时候因为是基本类型但是以为里面是泛型所以size指向的其实是引用,所以要为了节省空间将其删除
* @return
*/
public E remove(int index) {
if(index<0||index>=size) {
throw new IllegalArgumentException("index要大于零");
}
E num=data[index];
for (int i = index+1; i < size; i++) {
data[i-1]=data[i];
}
size--;
data[size]=null;
//loitering objects!=memory leak
//闲散游散元素不动于内存泄漏,只是为了优化而优化
//缩减的时候要注意后面吗还有没有元素,如果有元素就不能就行缩减
if(size==data.length/4&&data.length/2!=0) {
//记上了&&为了避免空
resize(data.length/2);
}
return num;
}
上面这段代码出现过,但是我的意思是告诉大家这是我们对自己的数组的一种优化,这个代码使我们学数据结构的基础,以后的出现的数据结构我会好好的整理的。