• 随笔③ java集合类 --- ArrayList,LinkedList,Vector,Hashtable,HashMap,ConcurrentHashMap


    java集合类 --- 继承关系

    java集合类 --- List接口

    java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
    java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

    一:ArrayList

    ① ArrayList有三个构造函数

    (1)ArrayList()构造一个初始容量为 10 的空列表。

    (2)ArrayList(Collection<? extends E> c)构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的

    (3)ArrayList(int initialCapacity)构造一个具有指定初始容量的空列表

    ② ArrayList是实现了基于动态数组的数据结构

    这里的所谓动态数组并不是那个“ 有多少元素就申请多少空间 ”的意思,通过查看源码,可以发现,这个动态数组是这样实现的,如果没指定数组大小,则申请默认大小为10的数组,当元素个数增加,数组无法存储时,系统会另个申请一个长度为当前长度1.5倍的数组,然后,把之前的数据拷贝到新建的数组中

    ③ ArrayList的Iterator实现

    在ArrayList内部首先是定义一个内部类Itr,该内部类实现Iterator接口,如下:

    1 private   class  Itr  implements  Iterator<E> {  
    2     //do something   
    3 }  

    而ArrayList的iterator()方法实现:

    1 public  Iterator<E> iterator() {  
    2      return   new  Itr();  
    3 }  

    所以通过使用ArrayList.iterator()方法返回的是Itr()内部类,所以现在我们需要关心的就是Itr()内部类的实现:

    在Itr内部定义了三个int型的变量:cursor、lastRet、expectedModCount。其中cursor表示下一个元素的索引位置,lastRet表示上一个元素的索引位置。

    1 int  cursor;               
    2 int  lastRet = - 1 ;       
    3 int  expectedModCount = modCount; 

    从cursor、lastRet定义可以看出,lastRet一直比cursor少一所以hasNext()实现方法异常简单,只需要判断cursor和lastRet是否相等即可。

    1 public   boolean  hasNext() {  
    2     return  cursor != size;  
    3 }  

    对于next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可,

     1 public  E next() {   
     2    checkForComodification();  
     3   int  i = cursor;     //记录索引位置    
     4    if  (i >= size)     //如果获取元素大于集合元素个数,则抛出异常   
     5    throw   new  NoSuchElementException();  
     6    Object[] elementData = ArrayList.this .elementData;  
     7    if  (i >= elementData.length)  
     8        throw   new  ConcurrentModificationException();  
     9    cursor = i + 1 ;       //cursor + 1   
    10    return  (E) elementData[lastRet = i];   //减少数据依赖cursor,有利于指令重排序lastRet + 1 且返回cursor处元素   
    11 }  

    二:LinkedList

    LinkedList是基于链表的数据结构。ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。

    三:ConcurrentHashMap

    jdk1.7中的ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。

    JDK6,7中的ConcurrentHashmap主要使用Segment来实现减小锁粒度,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。

    jdk7中ConcurrentHashmap中,当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能,所以jdk8 中完全重写了concurrentHashmap,代码量从原来的1000多行变成了 6000多 行,实现上也和原来的分段式存储有很大的区别

    主要设计上的变化有以下几点:

    改进一:取消segments字段,直接采用transient volatile HashEntry<K,V> table保存数据采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。

    改进二:将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数组中。如果hash之后散列的很均匀,那么table数组中的每个队列长度主要为0或者1。但实际情况并非总是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,但是在数据量过大或者运气不佳的情况下,还是会存在一些队列长度过长的情况,如果还是采用单向列表方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。

     四:Vector & ArrayList 的主要区别 

    1) 同步性:Vector是线程安全的,也就是说是同步的 ,而ArrayList 是线程序不安全的,不是同步的。 
    2)数据增长:当需要增长时,Vector默认增长为原来一倍 ,而ArrayList却是原来的50%  ,这样,ArrayList就有利于节约内存空间。 
          如果涉及到堆栈,队列等操作,应该考虑用Vector,如果需要快速随机访问元素,应该使用ArrayList 。
    【扩展知识】:

    1. Hashtable & HashMap 
    Hashtable和HashMap在它们的性能方面的比较类似Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。

    2. ArrayList & LinkedList

    ArrayList的内部实现是基于内部数组Object[],所以从概念上讲,它更象数组,但LinkedList的内部实现是基于一组连接的记录,所以,它更象一个链表结构,所以,它们在性能上有很大的差别:   
           从上面的分析可知,在ArrayList的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必然要花费较多时间,所以,当你的操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能; 而访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。

    五:HashMap与Hashtable

    1.  关于HashMap的一些说法:
    a)  HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap的底层结构是一个数组,数组中的每一项是一条链表。
    b)  HashMap的实例有两个参数影响其性能: “初始容量” 和 装填因子
    c)  HashMap实现不同步,线程不安全。 HashTable线程安全
    d) HashMap中的key-value都是存储在Entry中的
    e)  HashMap可以存null键和null值,不保证元素的顺序恒久不变,它的底层使用的是数组和链表,通过hashCode()方法和equals方法保证键的唯一性
    f) 解决冲突主要有三种方法:定址法,拉链法,再散列法。HashMap是采用拉链法解决哈希冲突的
    【注】: 链表法是将相同hash值的对象组成一个链表放在hash值对应的槽位;
    用开放定址法解决冲突的做法是:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。 沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。
    拉链法解决冲突的做法是: 将所有关键字为同义词的结点链接在同一个单链表中 。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数 组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。拉链法适合未规定元素的大小。

    2.  Hashtable和HashMap的区别:
    a) 继承不同。  public class Hashtable extends Dictionary implements Map
    public class HashMap extends AbstractMap implements Map
    b)  Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
    c)  Hashtable 中, key 和 value 都不允许出现 null 值。
    在 HashMap 中, null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null 。当 get() 方法返回 null 值时,即可以表示 HashMap 中没有该键,也可以表示该键所对应的值为 null 。因此,在 HashMap 中不能由 get() 方法来判断 HashMap 中是否存在某个键, 而应该用 containsKey() 方法来判断。
    d)  两个遍历方式的内部实现上不同。Hashtable、HashMap都使用了Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
    e)  哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值
    f)  Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。
    HashTable中hash数组默认大小是11,增加的方式是old*2+1。
    HashMap中hash数组的默认大小是16,而且一定是2的指数。
    【注】: HashSet子类依靠hashCode()和equal()方法来区分重复元素
    HashSet内部使用Map保存数据,即将HashSet的数据作为Map的key值保存,这也是HashSet中元素不能重复的原因。而Map中保存key值的,会去判断当前Map中是否含有该Key对象,内部是先通过key的hashCode,确定有相同的hashCode之后,再通过equals方法判断是否相同。
  • 相关阅读:
    组件基础
    css 手稿
    HTML手稿
    Vmstat命令监控Linux资源并将数据通过图形化方式显示
    JAVA---类和对象
    JAVA---Graphics2D类
    JAVA---数组
    JAVA---图形处理
    JAVA----日历源代码
    SQL常用语句大全
  • 原文地址:https://www.cnblogs.com/lyr612556/p/7816127.html
Copyright © 2020-2023  润新知