java中常用的存储容器就是数组的集合,每种容器存储的形式和结构又有所不同。
数组,是最基础的容器,在创建数组的时候有三种方式分别如下:
int[] arr = new int[5];
int[] arr = new String[]{1,2,3,4,5};
int[] arr = {1,2,3,4,5};
从上面的三种方式可以看出,在定义数组的时候有个共同的特点就是能够直接看出数组的长度,这也是数组的一大特点,就是定义的时候指定长度,同时数组一旦定义完成后长度就不可以变化,这也是数组在后期开发中较集合使用较少的原因,因为在实际开发中容器存储数据的长度存在较大的不确定性。在定义数组的方式中还可以看出定义数组的时候需要指定数组的类型,也就是数组内能够存储的数据类型,这样数组定义后,能够存储的数据类型就不能再变化,存在一定的局限性。
数组的内存存储:
接下来看看集合,集合是在开发较为常用的容器,先来看看集合的分类。
集合分类两个大类,分别是Collection集合和Map集合,Collection是使用单值的存储,而Map集合是使用键值对的形式存储,下面看看集合具体实现类的储存结构和特点。
Collection集合的共同特点:
1、存储的元素可以是不同的类型
2、集合的长度是变化的,不固定
3、都可以使用迭代器Iterator对集合中的元素进行遍历
ArrayList集合:在底层使用的数组结构进行存储,数组中存储的元素可以为null,且可以存多个重复的元素,存储的时候是按存储的先后顺序进行排序,每一元素都对应一个脚标值,在操作其中元素的时候可以通过脚标完成,在查询的时候,指定脚标,直接拿到指定的元素,无需遍历集合,因此在查询的时候效率高,增删效率低,因为在增删的过程中涉及集合元素的遍历。
LinkedList集合:在底层使用的是链表结构,有头有尾,也存在脚标,可以通过脚标操作,查询的效率较低,从源码分析上可知,首先判断脚标在中间位置的左侧还是右侧,然后对集合的一侧进行遍历,查询元素,在增删时直接操作指定的节点,无需遍历,效率提高。
ArrayList和LinkedList的比较示图:
ArrayList增加元素:
LinkedList添加元素:
Set集合:存储数据和List集合存在差距,List集合中可以存储重复的元素,而Set集合中不能存储重复的数据,List集合中存储的数据是有序的有脚标的,而Set集合中是无序的没有脚标的,不能通过脚标来操作集合中的元素
HashSet集合:在底层存储是使用的哈希表结构,存储的数据可以是null,由于唯一,因此只能存储一个,HashSet集合是如何保证元素唯一的呢?
HashSet底层使用了数组和哈希算法,在存储数据的时候会经过多次比较才能实现
具体实现:
1、先对存储的元素进行哈希算法得到一个哈希值
2、会使用这个计算得到的哈希值到存储数据的结构中找对应的位置,当该位置没有元素就直接添加进去
3、如果该位置已存在元素,会使用equals方法进行比较两个元素
4、如果再相等新添加的元素就不会被添加到集合,如果不相等就会重新计算添加元素的哈希值,然后进行添加
TreeSet集合:在底层使用了二叉树的存储结构,在保证存储数据不重复的机制上和HashSet是完全不同的
具体实现:
方式一:采用默认的比较器Comparable,这个时候需要存储的对象对应的类实现Comparable接口,并重新compareTo方法,在该方法中书写具体保证元素唯一的实现代码,若不实现和重写在存储元素的时候会出现异常
方式二:采用自定的比较器Comparator,此时具体的存储对象无需实现Comparator接口,可以另外自定义一个类,该类实现Comparator接口,并重写compare方法,在创建TreeSet集合的时候,将自定义类的对象作为参数传递给TreeSet集合的构造方法。但是该方式会多需要写一个类,较为麻烦,实现方式不友好,这个时候就可以考虑使用匿名内部类了。
两种实现方式会不会冲突呢,这个可能对初学者存在疑问,java既然这样设计,当然不会存在冲突的,看过源码的都会对这个比较机制的设计巧妙赞不绝口。
在创建TreeSet集合的时候,可以使用无参的构造方法也可以使用有参的构造方法(参数是比较器),在TreeSet中设置了一个用于保存比较器的变量(comparator),在存储元素的时候会调用一个比较的方法,该方法中对comparator进行判断,若comparator不为空,表示使用构造方法传入了比较器,就使用传入的比较器进行元素的唯一性比较,反之,就使用默认的比较器进行元素的唯一性比较。
Map集合:Map集合存储元素使用键值对的形式,键是唯一的,而值可以不唯一,键和值都可以为null,一个键只能对应一个值,但是一个值可以对应多个键,键值对在Map集合中是作为对象处理的,将一个键值对看做一个对象,Map集合中存储的是多个键值对对象。Map集合在遍历元素的时候没有类似于Collection集合的迭代器,而是使用keySet方法或者entrySet方法先转为Set集合,然后使用迭代器进行遍历。
HashMap集合:HashMap采用的也是哈希表机构,在保证键唯一性的操作上和HashSet相同。
TreeMap集合:TreeMap采用的是二叉树结构,在保证键的唯一性的操作上和TreeSet相同。
补充:
1、数组存储的元素可以是对象,也可以是基本数据类型,而集合只能存储对象
2、Collection集合都可以使用Iterator迭代器进行迭代,List集合也有它特有的迭代器ListIterator,该迭代器值适用于List集合,Collection下的其他集合不能使用
3、在开发的过程中使用ArrayList、HashSet、HashMap集合的较为普遍,其他的集合使用较少
4、LinkedList的API中有addFirst、removeFirst、addLast、removeLast四个特有的方法,因此可以使用该集合模拟栈和队列结构