基本数据结构的比较

基本数据结构的比较

基本数据结构

1. 列表(List)

  元素有放入顺序,元素可重复。html

数组实现(ArrayList类)

  JDK8源码中,初始长度是10,每次数组扩展都增长1/2左右。即:前端

private void grow(int minCapacity) {      //minCapacity为size+1,每次add元素都要检查
        int oldCapacity = elementData.length;   //扩展前数组的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);   //扩展后数组的容量约为原容量1.5倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
优势:

  能够经过下标来访问或者修改元素,对下标访问的set和get时间复杂度为O(1);java

缺点:
  1. 插入和删除的花费开销较大,除非变更是在ArrayList的末端进行。好比当在第一个位置前插入一个元素,那么首先要把全部的元素日后移动一个位置;数组扩展时,须要将原数组的元素所有复制到新数组。
  2. 数组要在连续的空间里存储集合的元素,因为数据存储是连续的,所以支持用下标访问元素;

数组实现(Vector 类)

  一样基于数组实现,会在内存中开辟一块连续的空间来存储。ArrayList是非线程安全的,效率高;Vector是基于线程安全的,但效率低,而且是方法级别的同步,不是绝对的线程安全
  初始容量10,每次数组扩展到原来容量的2倍(每次扩充的容量大小是能够设置的,而ArrayList类不支持设定)。算法

链表实现(LinkedList类)

  每个元素存储自己数据的同时还存储上、下两个元素的地址(双向链表)。编程

优势:
  1. 新项的插入和现有项的删除平均开销很小O(1)(假设变更项的位置已知),所以提供了addFirst和removeFirst, addLast和removeLast, getFirst 和 getLast 等有效添加、删除和访问两端的项的方法;
  2. 能够在非连续的内存空间里面存储一个集合的元素;
缺点:
  1. 根据索引的访问时间复杂度为O(n);
  2. 存放相同多的数据,通常状况下,数组占用较小的内存,而链表还须要存放其前驱和后继的空间。

2. 栈(Stack)

  栈,在计算机中运用普遍,好比说JVM,它就是基于栈来执行指令的。栈是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈顶,对栈的基本操做有push(进栈)和pop(出栈),前者至关于插入,后者至关于删除最后一个元素。栈有时又叫做LIFO(Last In First Out)表,即后进先出。
数组

  栈通常有两种实现,全部操做时间复杂度O(1):安全

  • 栈的链表实现:利用LinkedList类,经过表顶端的元素插入和删除。
  • 栈的数组实现:模仿ArrayList类,和栈相关的有两个元素,arrayList数组和topOfStack索引,初始状态topOfStack==-1,每次进栈一个元素x,topOfStack增1并令arrayList[topOfStack]=x;每次出栈一个元素,咱们置返回值arrayList[topOfStack],并令topOfStack减1。

3. 队列(Queue)

  对于队列来讲,元素只能从队列尾插入,从队列头访问和删除。普通的队列是一种先进先出(First In First Out,FIFO)的数据结构,而优先队列中,元素都被赋予优先级。当访问元素的时候,具备最高优先级的元素最早被删除。
  队列也是表,通常有两种实现,全部操做时间复杂度O(1)(优先队列是经过大顶堆或者小顶堆实现):数据结构

  • 队列的链表实现:利用LinkedList类,经过表尾端插入元素,前端删除元素,并记录队列中元素个数currentSize。
  • 队列的数组实现:保留一个数组theArray以及位置front和back,表明队列的两端;同时还要记录队列中元素个数currentSize。要使一个元素x入队,则currentSize和back增1,theArray[back]=x;要使一个元素出队,咱们置返回值theArray[front],且currentSize减1,、front增1。采用循环数组的方式,当front和back到达数组的尾端,他们又绕回开头。

4. 集合(Set)

  元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,可是元素在set中的位置是由该元素的HashCode决定的,其位置实际上是固定的)
  Set接口有两个实现类:HashSet和LinkedHashSet多线程

  • HashSet:(底层由HashMap实现),HashSet类按照哈希算法来存取集合中的对象,存取速度比较快 ,存入HashSet的对象必须定义hashCode()和equals()来确保对象的惟一性。
  • LinkedHashSet:具备HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。因而在使用迭代器遍历Set时,结果会按元素插入的次序显示。

  SortedSet接口有一个实现类:TreeSet 底层是经过 TreeMap来实现的(如同HashSet底层是是经过HashMap来实现的同样),所以两者的实现方式几乎彻底同样。并发

5. 映射(Map)

  元素按键值对存储,通常无放入顺序,其中值能够重复,但键是惟一的,不能重复。Map接口有三个实现类:HashMap,Hashtable,LinkeHashMap

  • HashMap:基于散列表实现,使用对象的“散列码”(hash code)来快速查询(默认使用的是Object的equals()和hashCode()方法,所以若是须要以本身定义的对象做为key,须要重写这两个方法,可是因为String字符串的这两个方法已经重写,以字符串做为key能够不重写),非线程安全,高效,容许有一个key设为null,初始容量16,负载因子0.75(好比容量16,能够存放16*0.75=12个数据,减小冲突),增长方式:通常old*2,因为容许设置初始容量,同时要保证容量增长后要是2的指数,因此容量增长比较复杂,详细看 http://www.cnblogs.com/xzwblog/p/7289952.html#_label2_0
  • Hashtable:一样基于散列表实现,但线程安全(一样是方法级别的同步),低效,不容许任何key设为null,初始容量11,负载因子0.75,增长方式是old*2+1
  • LinkeHashMap:LinkedHashMap是HashMap的一个子类,它保留插入的顺序。LinkedHashMap采用的hash算法和HashMap相同,可是它从新定义了数组中保存的元素Entry,该Entry除了保存当前对象的引用外,还保存了其上一个插入元素before和下一个插入元素after的引用,从而在哈希表的基础上又构成了双向连接列表。
  • SortedMap接口的实现类:TreeMap 的实现是红黑树算法,每一个 Entry 都被当成“红黑树”的一个节点对待,对key进行排序。插入、删除和查询都比较慢,复杂度O(logN),基于hash的复杂度通常为O(1)。但TreeMap 中的全部 Entry 老是按 key 根据指定排序规则保持有序状态,TreeSet 中全部元素老是根据指定排序规则保持有序状态。

HashMap和Hashtable的hash值计算方式也不相同
  HashMap的hash计算参考 http://www.cnblogs.com/xzwblog/p/7289952.html#_label2_0 ,速度更快。
  Hashtable是直接使用对象的hashCode,而且计算在hash表中的索引时直接使用%,以下代码:

int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;

  而且在高并发环境下,彻底能够用ConcurrentHashMap来代替Hashtable。

  还有一点不一样:HashMap去掉了Hashtable 的contains方法,可是加上了containsValue()和containsKey()方法。

如何实现HashMap的同步?
  HashMap能够经过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap);,具体而言,该方法返回一个同步的Map,该Map封装了底层的HashMap的全部方法,使得底层的HashMap即便是在多线程的环境中也是安全的。

比较

  Collection、List、Set、Map都是接口,不能实例化。
  JAVA集合只能存放引用类型的的数据,不能存放基本数据类型。
基本数据类类型:存的是数值自己,java中有四类八种基本数据类型,

  • 第一类:整型 byte short int long
  • 第二类:浮点型 float double
  • 第三类:逻辑型 boolean
  • 第四类:字符型 char

引用类型变量:存放的是数据的引用地址,并非数据的自己,引用类型变量是以间接方式去获取数据。引用类型变量都指向对象,如:数组、类、字符串等。

  Collection接口的经常使用方法:

  • boolean add(Object o)
  • void clear()
  • boolean isEmpty()
  • boolean contains(Object o)
  • Iterartor iterator()
  • boolean remove(Object o)
  • int size()
  • int hashCode();
  • boolean equals(Object o);
  • Object[] toArray() : 返回一个数组,该数组中包括集合中的全部元素

  List,Set是继承自Collection接口,Map不是。因此记不清队列和栈的添加和删除函数时,一概用add和remove。

总结:
  1. 若是涉及到堆栈,队列等操做,应该考虑用List,对于须要快速插入,删除元素,应该使用LinkedList,若是须要快速随机访问元素,应该使用ArrayList或者直接数组。
  2. 若是程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,若是多个线程可能同时操做一个类,应该使用同步的类。
  3. 在除须要排序时使用TreeSet,TreeMap外,都应使用HashSet,HashMap,由于他们的效率更高。
  4. 要特别注意对哈希表的操做,做为key的对象要正确复写equals和hashCode方法。
  5. 尽可能返回接口而非实际的类型,如返回List而非ArrayList,这样若是之后须要将ArrayList换成LinkedList时,客户端代码不用改变。这就是面向接口编程。
注意:

一、Collection没有get()方法来取得某个元素,只能经过iterator()遍历元素。 二、Set和Collection拥有如出一辙的接口。 三、List,能够经过get()方法来一次取出一个元素,使用数字来选择一堆对象中的一个,get(0)...。(add/get)。 四、通常使用LinkedList构造栈stack、队列queue。 五、Map用 put(k,v) / get(k),还可使用containsKey()/containsValue()来检查其中是否含有某个key/value。HashMap会利用对象的hashCode来快速找到key。 六、Map中元素,能够将key序列、value序列单独抽取出来。使用keySet()抽取key序列,将map中的全部keys生成一个Set。使用values()抽取value序列,将map中的全部values生成一个Collection。   为何一个生成Set,一个生成Collection?那是由于,key老是独一无二的,value容许重复。

相关文章
相关标签/搜索