Collection接口综述

Collection接口

Collection是集合类基本的接口,它不提供具体的实现,集合类都继承自Collection接口,Collection代表的是一种规则,它包含的元素必须符合某种规则,比如有的允许重复而有的不允许重复,有的排序而有的不排序,于是就衍生出两个子类接口,Set和List,先由下面一幅图看看Set和List的区别。

一、List接口

List是Collection的直接接口,它代表了有序可重复的Collection元素,元素会增加到特定的位置,即它使用特定的插入顺序来维护元素的排列顺序,可以精准的控制元素的位置,可以使用整数索引的方式来访问元素,实现List接口的主要集合有ArrayList、LinkedList,Vector、Stack。

2.1、ArrayList
ArrayList是一个动态数组,也是我们最常用的集合。它的基本操作有 size、isEmpty、get、set、 add、iterator 和 listIterator 操作它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。
ArrayList擅长于随机访问。同时ArrayList是非同步的。
2.2. LinkedList

LinkedList是一个双向链表,因为是链表的原因,LinkedList不能随机访问,如果要访问一个元素必须要从头到尾遍历直到找到这个元素为止,但是LinkedList也有一个好处,就是可以进行方便的插入和删除元素,所以它除了有ArrayList的基本操作方法外还额外提供了get,remove,add方法在LinkedList的首部或尾部,例如getFirst/getLast
和ArrayList一样LinkedList也是非同步的
如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
2.3 vector
和ArrayList一样,vector也是一个动态数组,但是不同的是,vector是同步,所以说vector是线程安全的的动态数组,它的操作和ArrayList一样。
2.4 stack
Stack继承自Vector,实现一个先进后出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

二、散列集

​ 在提set接口之前先说一下散列集,方便理解set的储存方式。因为set是无序的,且不可重复,那么set是用什么方法保证对象的无序且不可重复呢,答案就是通过散列码,散列码基于hashCode方法
散列表为每个对象计算一个整数,称为散列码,散列码是由对象的实例产生的一个整数,更准确的说,具有不同数据域的对象将产生不同的散列码。一般散列码是由hashCode方法产生,当我们向一个集合中添加某个元素,集合会首先调用hashCode方法,这样就可以直接定位它所存储的位置,若该处没有其他元素,则直接保存。若该处已经有元素存在,就调用equals方法来匹配这两个元素是否相同,相同则不存,不同则散列到其他位置。这样处理,当我们存入大量元素时就可以大大减少调用equals()方法的次数,极大地提高了效率。在java中hashCode的实现总是伴随着equals的,如果自己设计equals方法时,也要比较hashCode是否相等,在比较元素时,如果a.equals.(b)为true,a和b必须具有相同的散列码。

​ 所以hashCode在上面扮演的角色为寻域(寻找某个对象在集合中区域位置)。hashCode可以将集合分成若干个区域,每个对象都可以计算出他们的hash码,可以将hash码分组,每个分组对应着某个存储区域,根据一个对象的hash码就可以确定该对象所存储区域,这样就大大减少查询匹配元素的数量,提高了查询效率
处理流程是
​ 1、判断两个对象的hashcode是否相等,若不等,则认为两个对象不等,完毕,若相等,则比较equals。
​ 2、若两个对象的equals不等,则可以认为两个对象不等,否则认为他们相等。
​ 散列集对于List和数组来说不重要,甚至还是累赘,因为他们本身就是有序且可重复的,不需要这种比较方式,但是对于HashMap、HashSet、HashTable而言,它变得异常重要。所以在使用HashMap、HashSet、HashTable时一定要注意hashCode

三、set接口

  Set是一种不包括重复元素的Collection。它维持它自己的内部排序,所以随机访问没有任何意义。与List一样,它同样运行null的存在但是仅有一个。由于Set接口的特殊性,所有传入Set集合中的元素都必须不同,同时要注意任何可变对象,如果在对集合中元素进行操作时,导致e1.equals(e2)==true,则必定会产生某些问题。实现了Set接口的集合有:EnumSet、HashSet、TreeSet。

3.1、EnumSet

  是枚举的专用Set。所有的元素都是枚举类型。

3.2、HashSet

  HashSet堪称查询速度最快的集合,可以用add方法添加元素,因为其内部是以HashCode来实现的。它内部元素的顺序是由哈希码来决定的,所以它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。

3.3、TreeSet

  基于TreeMap,生成一个总是处于排序状态的set,内部以TreeMap来实现。它是使用元素的自然顺序对元素进行排序(红黑树),或者根据创建Set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

map接口

4.1、HashMap

  以哈希表数据结构实现,查找对象时通过哈希函数计算其位置,它是为快速查询而设计的,其内部定义了一个hash表数组(Entry[] table),元素会通过哈希转换函数将元素的哈希地址转换成数组中存放的索引,如果有冲突,则使用散列链表的形式将所有相同哈希地址的元素串起来,可能通过查看HashMap.Entry的源码它是一个单链表结构。

4.2、TreeMap

  键以某种排序规则排序,内部以red-black(红-黑)树数据结构实现,实现了SortedMap接口

4.3、HashTable

  也是以哈希表数据结构实现的,解决冲突时与HashMap也一样也是采用了散列链表的形式,不过性能比HashMap要低,现在基本上都是使用HashMap

集合中的异同点

6.1、Vector和ArrayList

  1,vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。
  2,如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。
  3,如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,都是0(1),这个时候使用vector和arraylist都可以。而如果移动一个指定位置的数据花费的时间为0(n-i)n为总长度,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据所花费的时间为0(1),而查询一个指定位置的数据时花费的时间为0(i)。
  	ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快!

6.2、Aarraylist和Linkedlist

  1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
  2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
  3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
  这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

6.3、HashMap与TreeMap

  1、HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。HashMap中元素的排列顺序是不固定的)。
  2、  HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。集合框架”提供两种常规的Map实现:HashMap和TreeMap (TreeMap实现SortedMap接口)。
  3、在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。 这个TreeMap没有调优选项,因为该树总处于平衡状态。

6.4、hashtable与hashmap

  1、历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现 。
  2、同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的 。
  3、值:只有HashMap可以让你将空值作为一个表的条目的key或value 。

对于集合的选择

7.1、对List的选择

  1、对于随机查询与迭代遍历操作,数组比所有的容器都要快。所以在随机访问中一般使用ArrayList
  2、LinkedList使用双向链表对元素的增加和删除提供了非常好的支持,而ArrayList执行增加和删除元素需要进行元素位移。
  3、对于Vector而已,我们一般都是避免使用。
  4、将ArrayList当做首选,毕竟对于集合元素而已我们都是进行遍历,只有当程序的性能因为List的频繁插入和删除而降低时,再考虑LinkedList。

7.2、对Set的选择

  1、HashSet由于使用HashCode实现,所以在某种程度上来说它的性能永远比TreeSet要好,尤其是进行增加和查找操作。
  3、虽然TreeSet没有HashSet性能好,但是由于它可以维持元素的排序,所以它还是存在用武之地的。

7.3、对Map的选择

posted @ 2019-12-21 16:36  穿黑风衣的牛奶  阅读(566)  评论(0编辑  收藏  举报