数组是数据结构中最基础的存储方式之一,集合是数组的延伸,散列表,又称哈希表(Hash Table),许多高级语言中都是在数组的基础上实现的,当然还有其他实现形式。
一、数组的特点:
1、定长
数组的的长度是固定的,数组的长度在声明时候已经确定,在使用的时候发现不够用,只能重新声明一个数组
2、按照顺序访问
在访问数组中某一个元素的时候,只能从第一个元素按照顺序访问,知道访问到指定位置的元素。虽然在写程序时候可以通过下标直接访问指定位置的元素,但计算机实际上还是按照顺序访问的。
二、升级版数组——集合
数组的致命缺点就是长度固定,如果我们一开始不确定,就变得很麻烦了。有可能你会说,那就来个动态的呗,动态数组,在C语言中有一种实现动态数组的方式,就是用Malloc函数动态开辟内存空间, 但在这里我要说的是另一种方式,集合。
什么是集合?
我的理解就是长度可变的列表(动态数组)
下面这些都是集合:
- 列表:一般的有序集合,特点是没有顺序,比如链表、队列、栈。
- 集:一般是无序的集合,特点就是没有顺序不能重复,多数语言都是散列表实现的,支持对集合进行添加、删除、查找等操作。
- 多重集:一般是无序的集合,但是数据可以有重复的值,支持对集进行添加、删除、查找包含、查找一个元素 在集合中的个数等操作。多重集通过排序可以转换为列表。
- 关联数组:多数语言也是用散列表实现的,就是可以通过键(key)获取到值(value)。也是没有顺序的
- 树、图:树、图 同样是集合
那集合是怎么实现的呢?
既然集合是长度可变的数组,那么我们就可以用数组实现集合。
下面,用ArrayList实现简单的集合,也可以叫它变长数组。
public class ArrayList {
private static final int INITIAL_SIZE = 10;
private int size = 0;
private int[] array;
public ArrayList() {
array = new int[INITIAL_SIZE];
}
public ArrayList(int initial){
if (initial <= 0) {
initial = INITIAL_SIZE;
}
array = new int[INITIAL_SIZE];
}
/**
* 添加一个元素
* @param num
*/
public void add(int num) {
if (size == array.length) {
array = Arrays.copyOf(array, size*2);
}
array[size++] = num;
}
/**
* 获取指定位置的元素值
* @param i
*/
public int get(int i) {
if (i >= size) {
throw new IndexOutOfBoundsException("获取的元素位置超过了最大长度");
}
return array[i];
}
/**
* 设置指定位置的元素值
* @param i
* @param num
* @return 指定的这个位置 之前的值
*/
public int set(int i,int num) {
int oldNum = get(i);
array[i] = num;
return oldNum;
}
/**
* 获取变长数组的长度
* @return
*/
public int size() {
return size;
}
}
public class ArrayListTest {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList(1);
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
System.out.println(arrayList.get(3));//获取数组下标为3的值,也就是第4个位置的值,打印4
arrayList.set(3, 9);//设置第4个位置的值为9
System.out.println(arrayList.get(3));//再次获取,这次打印9
System.out.println(arrayList.size());//变长数组的长度为5,实际上内部数组长度为8,在add(2)和add(5)时,分别进行了长度翻倍和数组复制
}
}
集合的特点:
- 集合的特点,跟它的实现有关,那就是变长,变长是相对而言,内部还是通过数组实现的,只是在不够用的时候根据策略生成一个更长的数组,把旧数组复制到新数组里。
- 在正常情况下,会有两个系统开销,一个是数组总是比我们实际使用的长度长,因此存在空间浪费,另一个是数组不够长的时候,需要新建一个更长的数组,同时把旧数组的数据复制到新数组中,这个操作比较消耗系统的性能。
3.
数组与变长数组的性能
- 虽然集合这个变长数组比普通数组更高级一些,但本质上还是基于数组实现的,所以和数组的性能差不多。
- 对数组的操作,并不是我们看到的那么直观,计算机需要根据我们的操作寻找指定位置,在数组中添加元素,修改元素,获取元素的时间复杂度都是O(n).
- 变长数组也有性能损耗的情况,在插入元素时发现其中固定数组的长度不够用,需要新建数组,复制数据,这个过程中会有性能损耗。