Java基础:常见集合总结

接口:Collection

Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection子接口ListSet

所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection

主要的一个接口方法:boolean add(Ojbect c)
虽然返回的是boolean,但不是表示添加成功与否,这个返回值表示的意义是add()执行后,集合的内容是否改变了(就是元素的数量、位置等有无变化)。类似的addAllremoveremoveAllremainAll也是一样的。

Iterator模式实现遍历集合

Collection有一个重要的方法:iterator(),返回一个Iterator(迭代器),用于遍历集合的所有元素。Iterator模式可以把访问逻辑从不同的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。

不需要维护遍历集合的指针,所有的内部状态都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成。

每一种集合类返回的Iterator具体类型可能不同,但它们都实现了Iterator接口,因此,我们不需要关心到底是哪种Iterator,它只需要获得这个Iterator接口即可,这就是接口的好处,面向对象的威力。

要确保遍历过程顺利完成,必须保证遍历过程中不更改集合的内容(Iterator的remove()方法除外),所以,确保遍历可靠的原则是:只在一个线程中使用这个集合,或者在多线程中对遍历代码进行同步。

Collection接口派生的两个接口是ListSet

List接口

List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素

除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。

实现List接口的常用类有LinkedListArrayListVectorStack

LinkedList

LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的getremoveinsert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。

注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List
List list = Collections.synchronizedList(new LinkedList(…));

ArrayList

ArrayList实现了可变大小的数组。它允许所有元素,包括null
sizeisEmptygetset方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。

每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。

LinkedList一样,ArrayList也是非同步的(unsynchronized

Vector

Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack

Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的pushpop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口

Set是一种元素不可重复Collection,即任意的两个元素e1e2都有e1.equals(e2)=falseSet最多有一个null元素

很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。

请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

(摘抄)set集合学习

hashSet

散列表,无序的数据存储结构.简单介绍下hashCode的概念:在Object中(如果该类没有覆盖hashCode()方法的话)表示某个对象内存的一个映射数字,通过该数字可以找到对应的对象内存块,类似于数据库中的索引的概念,当然有些类会重写hashCode方法,例如字符串,基本类型的封装类等。由于在集合类添加元素(包括但是不限于)等判断两个对象是否一样时我们是利用equals方法比较的,如果equals的比较结果为true,则hashCode也必须相同,这里就要求equals重写了则应该也要重写hashCode,以保证他们的对应关系。

  知道了hashCode的概念后,我们就可以大概了解hashSet的原理了:hashSet通过hashCode建立一个索引表,用于储存其内的所有对象的内存位置,add元素进来的时候,通过该表便可以快速地判断元素是否在set内,在取元素的时候,也可以通过该表快速定位对象了,这样可以保证其快速高效地添加和查询元素

  具体的set内部原理大概是这样的:java中会将set的内部存储机构分为多个链表(即链表数组)(我们下面称这些链表为桶),在添加元素时,通过元素的hashCode和桶的个数取余,得到的余数表示了该对象需要装到哪个桶中,只要保证桶数目够多,便可快速定位到该对象所在的位置,当发生hashCode重复时,调用冲突解决算法:该算法将hashCode相同但是eauqls为false的方法放在一个单独的链表中。具体请看下面的丑图:

好了,上图大概阐明了set的主要数据结构,归根到底就是一句话:set是利用hashCode进行对象定位,利用链表数组进行存储的数据结构!

  2、treeSet:树状的散列表(不知道这个描述是否准确)。treeSet,顾名思义,利用树的结构实现的一种set数据结构。在treeSet中,它不像hashSet中的元素那样元素毫无顺序,treeSet是一种有序的数据结构。具体的数据结构示意图就不画出来了,因为挺好理解的。总的来说,treeSet和hashSet大体上只是数据存储机构的不同,下面会对两者进行对比的。

  3、treeSet VS hashSet:

    首先,hashTree既然包含数组机构,那肯定在所有桶数到达临界点时,要进行大规模的扩桶操作,这样毫无疑问会大大影响性能。当然,也正因为它的无序性和包含链表的部分特性(数组链表嘛),它的插入元素是非常快的(毕竟插入时不用重新排序嘛),另外,它的查询操作速度上也还可以,毕竟它包含了一种数组的结构在里面(数组性质就是通过游标可以快速定位嘛);

    然后呢,再说treeSet吧,作为树状的结构,扩展时时不存在什么问题的,但是在插入元素的时候,显然要重新排列数据,所以插入输入肯定比不上hashSet的。当然,在查询元素中,treeSet的速度可不是盖的,毕竟人家是tree的结构(就算是简单的二叉有序树复杂度也只是logn)。

    总的来说:hashSet扩展性不好,但是查和插入(删除也一样)速度都不错;treeSet扩展性不错,查速度也不错,就是插入会速度慢点。

猜你喜欢

转载自blog.csdn.net/StringBuff/article/details/87252105