什么是集合
集合的概念有些宽泛。这里讲的集合主要是可变长度的列表(也叫做动态数组)。
下面这些都是集合:
-
列表:一般是有序的集合,特点就是有顺序,比如链表、队列、栈等。
-
集:一般是无序的集合,特点就是没有顺序并且数据不能重复,多数语言是使用散列表来实现的,支持对集进行添加、删除、查找包含等操作。
-
多重集:一般是无序的集合,特点是没有顺序,但是数据可以有重复的值,支持对集进行添加、删除、查找包含、查找一个元素在集中的个数等操作。多重集一般可以通过排序转换为列表。
-
关联数组:其实多数语言是使用散列表实现的,就是可以通过键(Key)获取到值(Value)。同样是没有顺序的。
树、图:同样是集合。
集合的实现
这里以java中的ArrayList为例,它是一个数组的列表,其实就是数组的拓展,或者说是可变长度的数组。
ArrayList数组是基于数组实现的,这如何理解呢?其实就是在ArrayList里有个属性,这个属性是一个数组。另外,还会有个属性记录我们放了多少数据,这样我们再向其中放数据时,就会知道该向这个内部数组的哪个位置放数据了,但是这个数组也会有长度限制,若超过了这个限制该怎么办呢?当超过这个限制时,其内部会创建一个具有更长的长度的数组,然后把旧数组的数据复制到新数组里面,这样就可以继续往里面放数据了。
下面我们通过流程图来形象的理解一下这个流程吧
下面我们先来简单地用代码实现这个变长数组。
package ArrayAndCollectionAndHashTable;
import java.util.Arrays;
//集合实现自动扩容
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) {
//传入一个整型参数,判断这个参数是否合理(是否大于0),若大于0则以其为数组长度
if(initial<=0) {
initial = INITIAL_SIZE;
}
array = new int[initial];
}
/*添加元素*/
public void add(int num) {
//如果数组长度与size相等,那么利用copyOf方法将原先数组数值复制至新创建的数组内,并将新数组的引用地址赋给原引用
if(size == array.length) {
array = Arrays.copyOf(array,size*2);
}
array[size++] = num;//添加一条数据 size自增1
}
/*获取指定位置的元素值*/
public int get(int i) {
//如果指定下标大于等于数组长度,那么会抛出异常“获取的元素位置超过了最大长度” 否则会返回指定数据
if(i>=size) {
throw new IndexOutOfBoundsException("获取的元素位置超过了最大长度");
}
return array[i];
}
/*设置指定位置的元素值*/
public int set(int i,int num) {
//
int oldNum = get(i);
array[i] = num;
return oldNum;
}
/*获取变长数组的长度*/
public int size() {
return size;
}
}
这里以整型为例简单实现了变长数组。
可以看到其中有两个属性:一个是array,就是内部数组;另一个是size,用来存当前变长数组的长度。当调用add向变长数组中放值时,要确认内部数组是否足够放这个值,若不够,就生成一个长度是原数组长度的两倍的新数组,并且复制旧数组的数据到新数组里,再放值。
这里新建数组并复制旧数组的数据,是通过java内部的一个工具类实现的,底层调用的是本地方法(native),效率很高。
package ArrayAndCollectionAndHashTable;
public class ArrayListTest {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList(1);//创建了一个数组长度为1的数组
//向数组下标为0的位置添加数据1;size自增1,由0变为1
arrayList.add(1);
/*上一条记录里面size为1,与当前数组长度相等,则会创建一个新数组,数组长度为2倍的size,将老数组内容复制并赋给新数组,然后再进行添加操作,
在数组下标为1的位置添加数据2;size自增为1,由1变为2*/
arrayList.add(2);
//数组当前长度为2,size为2,两者相等,将数组长度扩容至4;size自增1,变为3,数组存放第三个数据
arrayList.add(3);
//数组当前长度为4,size为3,存放第四个数据,size自增为4
arrayList.add(4);
//数组当前长度为4,size为4,两者相等,数组扩容至8,存放第五个数据,size自增为5
arrayList.add(5);
//获取arrayList数组下标为3的值并打印出来
System.out.println(arrayList.get(3));
//将arrayList数组下标为3的值替换为9
arrayList.set(3,9);
//获得arrayList下标为3的值并将其打印出来
System.out.println(arrayList.get(3));
//获得arrayList数组的长度(准确来说是数组存放最后一个元素+1的下标)并打印在控制台
System.out.println(arrayList.size());
}
}
上面的代码展示了如何调用变长数组来执行添加、修改、获取值、获取总长度操作。在调用过程中,我们完全不用在意其内部是怎么实现的,只需王里面添加值、获取值就好了。
ps:上面执行代码里面添加了很多的注释,这是我在学习的时候逻辑不太顺,添加注释方便自己理解,同学们可以不用在意。