一、前言
本篇文章总结目前学习的有关数组方面的知识,首先总结一下数组相关的核心概念,然后在封装一个自己的泛型动态数组类(ava已经封装的有现成的,自己封装只是为了加深理解),最后再学习解析下ArrayList源码。
本文应用:慕课网《玩转数据结构 从入门到进阶》课程,https://www.cnblogs.com/zhangyinhua/p/7687377.html
二、数组核心概念
1、数组就是将数据码成一排进行存放
2、因为数据是物理上连续排列的,那就可以给数据排序,添加索引,这样就可以通过索引快速访问数据。(如何通过索引快速访问呢?首个元素的地址+每个元素所占的地址宽度*索引值)
3、java声明数组的方式
int[] arr=new int[10];
4、Java中数组中存放的类型是固定的。在另外的一些语言,数组中是可以存放不同的数据类型的。
5、数组最大优点:快速查询。
6、索引最好有语义。
索引可以有语义,也可以无语义。
数组最好应用于“索引有语义”的情况,无语义一般适合其他的数据类型。但并非所有有语义的索引都适合于数组,例如以身份证号为索引。
7、数组一旦被定义,它的容量就不会在改变。
因此,除了结构的初始化和销毁之外,数组只有存取元素和修改元素值的操作。
8、在java中允许将一个数组变量拷贝给另一个数组变量,两个变量将引用同一个数组。
9、二维数组的两种存储方式:列序存储和行序存储(了解即可)
三、封装一个自己的数组类
java实际上已经为我们封装了一个Arrays类,但是为了加深了理解我们自己来封装一个数组类。
3.1、成员变量和基本方法
private E[] data; private int size; // 构造函数,传入数组的容量capacity构造Array public Array(int capacity){ data = (E[])new Object[capacity]; size = 0; } // 无参数的构造函数,默认数组的容量capacity=10 public Array(){ this(10); } // 获取数组的容量 public int getCapacity(){ return data.length; } // 获取数组中的元素个数 public int getSize(){ return size; } // 返回数组是否为空 public boolean isEmpty(){ return size == 0; }
3.2、扩容方法
为了解决数组类初始化后固定容量的局限性,我们通过本方法实现动态扩容,原理其实非常简单。
// 将数组空间的容量变成newCapacity大小 private void resize(int newCapacity){ E[] newData = (E[])new Object[newCapacity]; for(int i = 0 ; i < size ; i ++) newData[i] = data[i]; data = newData; }
3.3、add方法
注意在add方法中使用了resize方法实现动态扩容,默认为两倍原来容量。
// 在index索引的位置插入一个新元素e public void add(int index, E e){ if(index < 0 || index > size) throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size."); 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 addLast(E e){ add(size, e); } // 在所有元素前添加一个新元素 public void addFirst(E e){ add(0, e); }
3.4、查找方法
很简单就不多做解释了
// 获取index索引位置的元素 public E get(int index){ if(index < 0 || index >= size) throw new IllegalArgumentException("Get failed. Index is illegal."); return data[index]; }// 查找数组中是否有元素e public boolean contains(E e){ for(int i = 0 ; i < size ; i ++){ if(data[i].equals(e)) return true; } return false; } // 查找数组中元素e所在的索引,如果不存在元素e,则返回-1 public int find(E e){ for(int i = 0 ; i < size ; i ++){ if(data[i].equals(e)) return i; } return -1; }
3.5、删除方法
注意remove方法中使用resize方法实现减容。
// 从数组中删除index位置的元素, 返回删除的元素 public E remove(int index){ if(index < 0 || index >= size) throw new IllegalArgumentException("Remove failed. Index is illegal."); E ret = data[index]; for(int i = index + 1 ; i < size ; i ++) data[i - 1] = data[i]; size --; data[size] = null; // 方便GC if(size == data.length / 4 && data.length / 2 != 0) resize(data.length / 2); return ret; } // 从数组中删除第一个元素, 返回删除的元素 public E removeFirst(){ return remove(0); } // 从数组中删除最后一个元素, 返回删除的元素 public E removeLast(){ return remove(size - 1); } // 从数组中删除元素e public void removeElement(E e){ int index = find(e); if(index != -1) remove(index); }
四、结尾
本文的内容还是很简单的,本来想一起把ArrayList源码解析一起写完的,但是发现这样篇幅太长了,所以放到下一篇文章:【java集合总结】-- ArrayList源码解析