一讲到装东西的容器,你可能习惯于使用ArrayList和数组,你有想过ArrayList和数组的区别吗?
Java的类起名字都不是随便乱起的,通常前面是辅助,后面是实质:ArrayList = Array + List
Array
就是数组,List
即是表结构,ArrayList即数组实现的表结构
,问题来了,什么是表结构
注:不要问我效果图用什么软件画的...由于原本就没用什么软件画。下一节带你一块儿本身画
!!!
但愿你能够和我在Github一同见证:DS4Android的诞生与成长,欢迎stargit
表结构的常规操做github
数组的扩容与缩容编程
课程表,成绩表,做息时间表、列车行程表、手表(这个算了吧...)
复制代码
能够把同类的对象统一管理,好比成绩表:
高三12班的54为同窗的成绩是对象,对象又包括数学、语文、英语...等属性
把混乱的54个对象放在一块儿,这么一排,哪一个是学霸,哪一个是学渣一目了然,很是方便
若是其中某个对象的成绩改错了,能够get到对象从新set一下,也很是方便
若是中间一我的做弊了,分数取消,直接remove掉,后面的人名词往前排,也很是方便
若是高三12班和高三11班要比较成绩,两张表contact一下,就成一张表,合并也很方便
复制代码
打个最恰当的比方就是:
数组至关于打印出来的纸质版
而表结构像是Excel中可操做版
数组
1.数组定长:添加新元素,定位添加都很困难
2.拿删除来讲:数组remove掉了,后面的人名次都不变----(我还没个空白名次高,你说气人不气人...)
3.表是一种抽象数据类型(Abstract Data Type),既然是抽象就是规范或功能,表会有不一样的实现形式
[番外]:小和尚问老和尚:"什么是圣人?" 老和尚说:"好好学习,每天向上,乐于助人,诚信友善"
这里"圣人"即是一种抽象,"好好学习,每天向上,乐于助人,诚信友善"即是"圣人"的"条件(功能)",
小和尚按照这么作了,他就是老和尚眼中的"圣人",即小和尚实现了圣人。
4.一样,表是一种抽象,也能够定义你眼中的表,并为它附上add(),get(),set(),remove()等功能
5.其实Java的ArrayList实现了List这个抽象接口
复制代码
因为Java用List,为了避免混淆,取了个新名字叫Chartbash
也就是说说你的表能干吗用(接口方法最好注释很是清晰)微信
/**
* 做者:张风捷特烈
* 时间:2018/9/25 0025:8:25
* 邮箱:1981462002@qq.com
* 说明:线性表结构接口
*/
public interface IChart<T> {
//region -------------添加操做------------
/**
* 定点添加
*
* @param index 索引
* @param el 数据元素
*/
void add(int index, T el);
/**
* 添加尾
*
* @param el 数据元素
*/
void add(T el);
//endregion
//region -------------删除操做------------
/**
* 定位删除
*
* @param index 索引
* @return 删除的元素
*/
T remove(int index);
/**
* 删除尾位
*
* @return 删除的元素
*/
T remove();
/**
* 删除指定元素的第一次出现时
*
* @param el 数据元素
* @return 元素位置
*/
int removeEl(T el);
/**
* 删除全部指定元素
*
* @param el 数据元素
*/
boolean removeEls(T el);
/**
* 清空集合
*/
void clear();
//endregion
//region -------------改查操做------------
/**
* 设置某位置的元素新值
*
* @param index 索引
* @param el 新值
* @return 旧值
*/
T set(int index, T el);
/**
* 根据指定位置获取元素
*
* @param index 索引
* @return 数据元素
*/
T get(int index);
/**
* 根据指定元素获取匹配索引
*
* @param el 数据元素
* @return 索引集
*/
int[] getIndex(T el);
//endregion
//region ------------其余操做-------------
/**
* 集合是否包含某元素
*
* @param el 数据元素
* @return 是否包含
*/
public boolean contains(T el);
/**
* 链接两个集合
*
* @param iChart 插入集合
* @return 合并后的集合
*/
public IChart<T> contact(IChart<T> iChart);
/**
* 定点链接两个集合
*
* @param index 索引
* @param iChart 插入集合
* @return 合并后的集合
*/
IChart<T> contact(int index, IChart<T> iChart);
/**
* 是否为空
*
* @return 是否为空
*/
boolean isEmpty();
/**
* 返回集合大小
*
* @return 大小
*/
int size();
/**
* 获取数组容量
* @return 数组容量
*/
int capacity();
//endregion
}
复制代码
实现接口,并实现接口里的全部方法数据结构
/**
* 做者:张风捷特烈<br/>
* 时间:2018/11/21 0021:8:18<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:数组实现线性表结构
*/
public class ArrayChart<T> implements IChart<T> {
//空实现---略
}
复制代码
private int size;//表中数据的个数
private T[] data;//数据核心承载体
private static final int DEFAULT_CAPACITY = 10;//默认数组容量
private static final float GROW_RATE = 1.5f;//扩容增加率
public ArrayChart() {
this(DEFAULT_CAPACITY);//无参构造默认建立10个容量的数组
}
public ArrayChart(int capacity) {
data = (T[]) new Object[capacity];//新建立[数组表]时初始化数组
}
复制代码
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public int size() {
return size;
}
@Override
public int capacity() {
return data.length;
}
复制代码
看一下操做图(
将在下一篇:视图篇完成
):默认添加到尾部
思路:定点后的全部元素后移一位,空出顶点位,让待添加元素入驻
紫色框表明空的数组位,中间填充的是表中的实际元素
可见定点添加是在选中索引的前一位添加,因此添加到尾部是add(size,data)来添加ide
@Override
public void add(T el) {
add(size , el);//这里size---是由于在size以前一位添加
}
@Override
public void add(int index, T el) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("Add failed! Make sure index < 0 || index > size");
}
//从最后一个元素开始,到定点位置元素,元素都后移一位
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = el;
size++;
}
复制代码
@Override
public T set(int index, T el) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Set failed! Make sure index < 0 || index > size");
}
T oldEl = get(index);
data[index] = el;//设置一下,很简单
return oldEl;
}
@Override
public T get(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Get failed! Make sure index < 0 || index > size");
}
return data[index];//查询数组的对应索引处
}
复制代码
定值查询获取索引post
@Override
public int[] getIndex(T el) {
int[] tempArray = new int[size];//临时数组
int count = 0;//重复个数
for (int i = 0; i < size; i++) {//遍历集合,获取该元素重复个数,及位置数组
if (data[i].equals(el)) {
tempArray[count] = i;
count++;
}
}
//将临时数组压缩---排除空位
int[] indexArray = new int[count];
for (int i = 0; i < count; i++) {
indexArray[i] = tempArray[i];
}
return indexArray;//返回查找元素的索引数组(至关于成绩表看数学80分的都有哪些人)
}
复制代码
@Override
public T remove() {
return remove(size - 1);
}
@Override
public T remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Remove failed! Make sure index < 0 || index > size");
}
T temp = get(index);
//从删除元素索引的下一位开始到结尾,依次左移
// 可简写: System.arraycopy(data, index + 1, data, index + 1 - 1, size - index + 1);
for (int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
size--;
//置空--游荡的对象
data[size] = null;
return temp;
}
复制代码
其余删除: 定元素删除单个和定元素删除全部至关于前面的组合操做,就不作操做演示了学习
@Override
public int removeEl(T el) {
int[] indexes = getIndex(el);//查找元素的索引集合,删除首个
int index = -1;
if (indexes.length > 0) {
index = indexes[0];
remove(indexes[0]);
}
return index;
}
@Override
public boolean removeEls(T el) { //查找元素的索引集合,删除全部
int[] indexArray = getIndex(el);
if (indexArray.length != 0) {
for (int i = 0; i < indexArray.length; i++) {
remove(indexArray[i] - i); // 注意-i
}
return true;
}
return false;
}
复制代码
也没有什么高大上的,就是一个篮子装不下了,装个更大的篮子装而已
/**
* 扩容/缩容
*
* @param newCapacity 新容量
*/
private void grow(int newCapacity) {
T[] newData = (T[]) new Object[newCapacity];//建立个大篮子
for (int i = 0; i < size; i++) {//把原来的元素放到大篮子里
newData[i] = data[i];
}
data = newData;
}
复制代码
何时扩容----篮子不够装了呗---add
何时须要缩容----1000个容量的篮子装1个鸡蛋想一想也浪费---remove
@Override
public void add(int index, T el) {
if (size == data.length) {//篮子装不下了---
grow((int) (GROW_RATE * data.length));//换个1.5倍的篮子
}
if (index < 0 || index > size) {
throw new IllegalArgumentException("Add failed! Make sure index < 0 || index > size");
}
//从最后一个元素开始,到定点位置元素,元素都后移一位
//可简写:System.arraycopy(data, index, data, index + 1, size - index);
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = el;
size++;
}
复制代码
这里的判断标志是留多点备用,否则有可能插入移除频繁而致使重复扩容或缩容,
篮子可能会说:"你到底缩仍是放,你是否是在玩老子....,老子给你多留点空行了吧!"
@Override
public T remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("Remove failed! Make sure index < 0 || index > size");
}
T temp = get(index);
//从删除元素索引的下一位开始到结尾,依次左移
// 可简写: System.arraycopy(data, index + 1, data, index + 1 - 1, size - index + 1);
for (int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
size--;
//置空--游荡的对象
data[size] = null;
//缩容----此处限定是为了不反复出现扩容缩容---可自定义
if (size == data.length / 4 && data.length / 2 != 0 && data.length > 5) {
grow(data.length / 2);
}
return temp;
}
复制代码
@Override
public void clear() {
size = 0;
grow(DEFAULT_CAPACITY);
}
复制代码
@Override
public boolean contains(T el) {
return getIndex(el).length != 0;//按值查询有数据
}
复制代码
@Override
public IChart<T> contact(IChart<T> iChart) {
return contact(size - 1, iChart);
}
@Override
public IChart<T> contact(int index, IChart<T> iChart) {
if (!(iChart instanceof ArrayChart)) {//必须是数组才能联合
return null;
}
//从index处遍历本数组,将待插入数据一个一个插入
for (int i = index; i < index + iChart.size(); i++) {
add(i + 1, iChart.get(i - index));
}
return this;
}
复制代码
做为一个表结构,基本上就演示这么多,还有其余操做能够自定义接口,本身实现,
不过无论多么复杂的操做都是以上操做的组合而已。
关于复杂度的分析,等到全部表结构讲完再总体比较一下,这里先粗略感受一下
方法\操做次数 | 1000 | 10000 | 10W | 100W | 1000W |
---|---|---|---|---|---|
add首 | 0.0063秒 | 0.2706秒 | 19.5379秒 | ---- | ---- |
add尾 | 0.0004秒 | 0.0025秒 | 0.0141秒 | 0.0687秒 | 1.26014秒 |
remove首 | 0.0063秒 | 0.2771秒 | 19.7902秒 | ---- | ---- |
remove尾 | 0.0005秒 | 0.0036秒 | 0.0091秒 | 0.02301秒 | :0.1607秒 |
能够看出往开始添加/删除会很困难,从代码中能够感受到,毕竟要让后面全部人挪一挪
想一下若是30000人排一块儿,第一我的走了,后面全部人往前挪一下,是否是工程量挺大的
要是你决定插到第一个,让后面的人都日后移一下.....(大哥,活着难道很差吗....)
因此频繁对第一个元素进行操做的,仍是不要做死,数组表结构(ArrayList)不适合你
项目源码 | 日期 | 备注 |
---|---|---|
V0.1--github | 2018-11-21 | 看得见的数据结构Android版之表的数组实现(数据结构篇) |
笔名 | 微信 | 爱好 | |
---|---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 | 语言 |
个人github | 个人简书 | 个人掘金 | 我的网站 |
1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----我的能力有限,若有不正之处欢迎你们批评指证,一定虚心改正
4----看到这里,我在此感谢你的喜欢与支持