gzdjava集合框架的一些源码类型分析.docx
《gzdjava集合框架的一些源码类型分析.docx》由会员分享,可在线阅读,更多相关《gzdjava集合框架的一些源码类型分析.docx(14页珍藏版)》请在冰豆网上搜索。
gzdjava集合框架的一些源码类型分析
集合框架
HashMap、HashTable、LinkedHashMap
1、HashMap与HashTable的异同。
都实现了Map接口
HashTable继承自dictionary类,而HashMap是Java1.2引进的Mapinterface的一个实现。
HashMap允许将null作为Key或者Value,但HashTable不可以
HashMap将HashTable的Contains方法去掉了,改成了ContainsKey和ContainsValue.
最大的不同是:
HashTable方法是synchronize的,在多线程访问HashTable时,不需要自己为他的方法实现同步,而HashMap就必须为之同步.
注:
HasHTanle所有的方法都是synchronized的,下图只是其中之一。
Collection和map
Collection一组对立的u元素,这些元素都服从某些规则,List必须保持元素的顺序性,而Set不可以有重复的元素
collection对象之间没有指定的顺序,也可以重复元素
set对象之间没有指定的顺序,但不可以有重复元素
List对象之间有指定的顺序,因为引入了角标,可以有重复的元素
Map概念其实很很清晰,其实也可以把Map当成Collection的一个元素,只不过这个元素是一个键值对!
但是这样就太混乱了,也太笨拙了,所以把Map作为单个的一个概念,似乎更加清晰,而且Map也很容易扩展,value的值可以是另一个Map,如此下去,甚至无线扩展。
(注:
List、Set、Map共同的实现基础是Object数组)
除了四个历史集合类外,Java2框架还引入了六个集合实现,如下表所示。
接口
实现
历史集合类
Set
HashSet
TreeSet
List
ArrayList
Vector
LinkedList
Stack
Map
HashMap
Hashtable
TreeMap
Properties
集合详解之collection
collevction的实现基础是Object数组,所以他肯定有toArray方法
任何容器,都能将元素放进去、取出来,而且collection没有get()方法,要想从中去除元素,只有遍历了,所以Collection接口提供了iterator方法,此方法返回一个iterator对象,利用iterator接口的接口的方法,对Collection进行遍历。
而iterator接口的方法很少,只有booleanhasNext();Enext();voidremove();。
集合详解之List
List接口只继承了Collection接口
两种常对List接口的实现
简述
实现
操作特性
成员要求
List
提供基于索引的对成员的随机访问
ArrayList
提供快速基于索引的成员访问,对头尾的增加删除等最好
任意Object自雷的对象
LinkedList
对任何位置的增加删除支持特别好,但是基于索引的成员访问功能差
任意Object子类的对象
ArrayList简单分析实现原理
ArrayList的3个构造函数
这个构造函数比较重要,但是不常用。
其中intinitialCapacity表示构造这个ArrayList初始列表的容量,理解这个参数的方式得看ArrayList的两个属性,第一个属性
,我们使用elementData[]这个数组来存放集合中的数据,而intinitialCapacity就是这个数组的lengh!
默认值为10!
下面说第二个属性
size表示列表中真是数据的存放个数!
这个构造函数的作用就是把另一个容器对象中的元素放到当前List对象中去!
ElementData[]被容器CtoArray后赋值,然后ArrayList的实际尺寸Size为此时的ElecmentData[]的长度,然后利用elementData[],ArrayList的尺寸Size,和获取的class来创建一个ArrayList!
!
!
!
疑问?
?
既然ArrayList是基于数组来实现的,那么elementData[]数组的长度是固定的,而怎么能通过Size来最后确定ArrayList尺寸?
?
既然这样,那就是elementData[]有它的扩容机制!
!
先说ArrayList是继承了抽象类AbstractList而在这个抽象类中有modcount的变量
并且被初始化为0,这个变量表示对elementData数组进行了多少次操作,比如扩容、清空。
。
。
。
。
。
。
。
再看源码
首先得到当前elementData 属性的长度oldCapacity。
然后通过判断oldCapacity和mi-nCapacity参数谁大来决定是否需要扩容
如果minCapacity大于oldCapacity,那么我们就对当前的List对象进行扩容。
扩容的的策略为:
取(oldCapacity*3)/2+1和minCapacity之间更大的那个。
然后使用数组拷贝的方法,把以前存放的数据转移到新的数组对象中
如果minCapacity不大于oldCapacity那么就不进行扩容。
集合讲解之Map
Map简单原理
从图中可以知道,他和collection没有一点关系!
!
!
下面为Map几种常用实现类的比较
简述
实现
操作特性
成员要求
Map
保存键值对成员,基于键找值操作,使用compareTo或compare方法对键进行排序
HashMap
能满足用户对Map的通用需求
键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeMap
支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作
键成员要求实现Comparable接口,或者使用Comparator构造TreeMap键成员一般为同一类型。
LinkedHashMap
保留键的插入顺序,用equals方法检查键和值的相等性
成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
注意:
treeMap是通过key来排序的,所以要求Key对象要实现Comparable接口!
!
HashMap的实现原理
Hash算法和Hash表
Hash算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系,(每一个真实值只能有一个键值,但是一个键值可以对应多个真实值),这样可以快速在数组等里面存取数据。
HashMap属性1
用来存放键值对的对象Entry数组,也就是Hash表
在Map接口中有内部接口
,用它来保存我们的键值对!
!
HashMap属性2
当前Map中存放的键值对的个数
HashMap属性3
负载因子,用来决定什么情况下应该对Entry进行扩容
主要构造函数
1. 首先是判断参数intinitialCapacity和floatloadFactor是否合法
2. 然后确定Hash表的初始化长度。
确定的策略是:
通过传进来的参数initialCapacity来找出第一个大于它的2的次方的数。
比如说我们传了18这样的一个initialCapacity参数,那么真实的table数组的长度为2的5次方,即32。
之所以采用这种策略来构建Hash表的长度,是因为2的次方的运算对于现代的处理器来说,可以通过一些方法得到更加好的执行效率。
3. 接下来就是得到重构因子(threshold)了,这个属性也是HashMap中的一个比较重要的属性,它表示,当Hash表中的元素被存放了多少个之后,我们就需要对该Hash表进行重构。
4. 最后就是使用得到的初始化参数capacity来构建Hash表:
Entry[]table。
如何添加键值对
1. 首先我们判断如果key为null则使用一个常量来代替该key值,该行为在方法maskNull()终将key替换为一个非null的对象k。
2. 计算key值的Hash码:
hash
3. 通过使用Hash码来定位,我们应该把当前的键值对存放到Hash表中的哪个格子中。
indexFor()方法计算出的结果:
i就是Hash表(table)中的下标。
4. 然后遍历当前的Hash表中table[i]格中的链表。
从中判断已否已经存在一样的键(Key)的键值对。
如果存在一样的key的键,那么就用新的value覆写老的value,并把老的value返回
5. 如果遍历后发现没有存在同样的键值对,那么就增加当前键值对到Hash表中的第i个格子中的链表中。
并返回null。
最后我们看看一个键值对是如何添加到各个格子中的链表中的:
我们先看voidaddEntry(inthash,Objectkey,Objectvalue,intbucketIndex)方法,该方法的作用就用来添加一个键值对到Hash表的第bucketIndex个格子中的链表中去。
这个方法作的工作就是:
1. 创建一个Entry对象用来存放键值对。
2. 添加该键值对----Entry对象到链表中
3. 最后在size属性加一,并判断是否需要对当前的Hash表进行重构。
如果需要就在voidresize(intnewCapacity)方法中进行重构。
之所以需要重构,也是基于性能考虑。
大家可以考虑这样一种情况,假定我们的Hash表只有4个格子,那么我们所有的数据都是放到这4个格子中。
如果存储的数据量比较大的话,例如100。
这个时候,我们就会发现,在这个Hash表中的4个格子存放的4个长长的链表。
而我们每次查找元素的时候,其实相当于就是遍历链表了。
这种情况下,我们用这个Hash表来存取数据的性能实际上和使用链表差不多了。
但是如果我们对这个Hash表进行重构,换为使用Hash表长度为200的表来存储这100个数据,那么平均2个格子里面才会存放一个数据。
这个时候我们查找的数据的速度就会非常的快。
因为基本上每个格子中存放的链表都不会很长,所以我们遍历链表的次数也就很少,这样也就加快了查找速度。
但是这个时候又存在了另外的一个问题。
我们使用了至少200个数据的空间来存放100个数据,这样就造成至少100个数据空间的浪费。
在速度和空间上面,我们需要找到一个适合自己的中间值。
在HashMap中我们通过负载因子(loadFactor)来决定应该什么时候应该重构我们的Hash表,以达到比较好的性能状态。
集合之Set
简述
实现
操作特性
成员要求
Set
成员不能重复
HashSet
外部无序地遍历成员。
成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeSet
外部有序地遍历成员;
附加实现了SortedSet,支持子集等要求顺序的操作
成员要求实现Comparable接口,或者使用Comparator构造TreeSet。
成员一般为同一类型。
LinkedHashSet
外部按成员的插入顺序遍历成员
成员与HashSet成员类似
Java中Set的概念和数学中的集合(set)一致,都表示一个集内可以存放的元素是不能重复的。
前面我们会发现,Set中很多实现类和Map中的一些实现类的使用上非常的相似。
而且前面再讲解Map的时候,我们也提到:
Map中的“键值对”,其中的“键”是不能重复的。
这个和Set中的元素不能重复一致。
我们以HashSet为例来分析一下,会发现其实Set利用的就是Map中“键”不能重复的特性来实现的。
先看看HashSet中的有哪些属性:
再结合构造函数来看看:
通过这些方法,我们可以发现,其实HashSet的实现,全部的操作都是基于HashMap来进行的。
我们看看是如何通过HashMap来保证我们的HashSet的元素不重复性的:
看到这个操作我们可以发现HashSet的巧妙实现:
就是建立一个“键值对”,“键”就是我们要存入的对象,“值”则是一个常量。
这样可以确保,我们所需要的存储的信息之是“键”。
而“键”在Map中是不能重复的,这就保证了我们存入Set中的所有的元素都不重复。
而判断是否添加元素成功,则是通过判断我们向Map中存入的“键值对”是否已经存在,如果存在的话,那么返回值肯定是常量:
PRESENT,表示添加失败。
如果不存在,返回值就为null表示添加成功。
我们再看看其他的方法实现:
了解了这些后,我们就不难理解,为什么HashMap中需要注意的地方,在HashSet中也同样的需要注意。
其他的Set的实现类也是差不多的原理。
至此对于Set我们就应该能够比较好的理解了。
总结:
集合框架中常用类比较
用“集合框架”设计软件时,记住该框架四个基本接口的下列层次结构关系会有用处:
Collection接口是一组允许重复的对象。
Set接口继承Collection,但不允许重复。
List接口继承Collection,允许重复,并引入位置下标。
Map接口既不继承Set也不继承Collection,存取的是键值对
我们以下面这个图表来描述一下常用的集合的实现类之间的区别:
Collection/Map
接口
成员重复性
元素存放顺序(Ordered/Sorted)
元素中被调用的方法
基于那中数据结构来实现的
HashSet
Set
Uniqueelements
Noorder
equals()
hashCode()
Hash表
LinkedHashSet
Set
Uniqueelements
Insertionorder
equals()
hashCode()
Hash表和双向链表
TreeSet
SortedSet
Uniqueelements
Sorted
equals()
compareTo()
平衡树(Balancedtree)
ArrayList
List
Allowed
Insertionorder
equals()
数组
LinkedList
List
Allowed
Insertionorder
equals()
链表
Vector
List
Allowed
Insertionorder
equals()
数组
HashMap
Map
Uniquekeys
Noorder
equals()
hashCode()
Hash表
LinkedHashMap
Map
Uniquekeys
Keyinsertionorder/Accessorderofentries
equals()
hashCode()
Hash表和双向链表
Hashtable
Map
Uniquekeys
Noorder
equals()
hashCode()
Hash表
TreeMap
SortedMap
Uniquekeys
Sortedinkeyorder
equals()
compareTo()
平衡树(Balancedtree)