java集合框架解析

来源:互联网 发布:域名别名解析设置 编辑:程序博客网 时间:2024/06/05 19:01

Java集合框架如下图所示
http://blog.csdn.net/u013256816/article
http://blog.csdn.net/jackfrued/article/details/44921941

Map系:HashMap, LinkedHashMap, TreeMap, WeakHashMap, EnumMap;
List系:ArrayList, LinkedList, Vector, Stack;
Set系:HashSet, LinkedHashSet, TreeSet;
工具类:Collections,Arrays

Java集合架构支持3种类型的集合:规则集(set),线性表(list),和图(map),
set实列存储一组互不相同的元素
list实列存储一组顺序排列的元素
map存储一组key value

先来说Collection接口,它是处理对象集合的根接口,提供了一些公用方法,size,Iterator,add,remove什么的

Set和List接口都扩展自Collection,Set就是高中数学里所说的集合,不允许重复,无序。List就像一个表,可以重复,元素在表里有顺序的放着。

然后来说Set接口的3种实现:
HashSet的对象必须实现hashCode方法,javaAPI大多数类实现了hashCode方法。
LinkedHashSet实现了对HashSet的扩展,支持规则集内元素的排序,在HashSet中元素是没有顺序的,而在LinkedHashSet中,可以按元素插入集合的顺序进行提取
TreeSet保证集中的元素是有序的,有2种方法可以实现对象之间的可比较性:1,添加到TreeSet的对象实现了Comparable接口;2,给规则集的元素指定一个比较器(Comparator)

如果希望按照元素插入集合的顺序进行提取元素,用LinkedHashSet,它的元素按添加的顺序存储
如果没有上述需求,应该用HashSet,它的效率比LinkedHashSet高
LinkedHashSet只是按照添加的的先后顺序在存储时保持顺序,要给集合元素添加顺序属性,需要使用TreeSet(集合元素有排序关系)。

再来说List的几种实现
最重要的的当然是ArrayList(不同步)和LinkedList,一个使用数组实现的动态扩展容量的list,一个是链式实现的list。
还有就是Vector(同步)类,它除了包含访问和修改向量的同步方法之外,跟ArrayList一样。
最后就是Stack类,它继承自Vector类,,但一般只作为栈的功能来使用,不要去使用Vector里面的功能

Map是映射,跟前面的Set和List有本质的区别。
散列图HashMap,链式散列图LinkedHashMap,树形图TreeHashMap是映射的3种实现,从名字上来说,有了上述Set的3种实现的分析,这个也是类似的。
HashMap:效率高
LikedHashMap:按照添加顺序存储,可以按添加顺序取出
TreeHashMap:排序性

Collections类和Arrays类:
Collections类(注意不是Collection):提供了许多静态的方法来管理集合,线性表(大多数是来操作线性表的,比如对线性表复制,排序之类的,参见API)
Arrays类:提供了对数组排序,查找,比较,填充元素的各种静态方法。

HashMap的工作原理:
http://www.importnew.com/7099.html
在HashMap中有两个很重要的参数,容量(Capacity)和负载因子(Load factor)。
Capacity的默认值为16:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
负载因子的默认值为0.75:
static final float DEFAULT_LOAD_FACTOR = 0.75f;

如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
如果超过了负载因子(默认0.75),则会重新resize一个原来长度两倍的HashMap,并且重新调用hash方法。

简单的说,Capacity就是bucket的大小,Load factor就是bucket填满程度的最大比例
如果对迭代性能要求很高的话不要把Capacity设置过大,也不要把load factor设置过小。当bucket中的entries的数目大于capacity*load factor时就需要调整bucket的大小为当前的2倍。

可以设置初始容量Capacity,但是在HashMap处理过程中,是会把Capacity扩充成2的倍数,怎么理解?比如你设置的初始值17,但是17不是2的整数倍,会扩容成32,再比如你初始设置的是15,会扩容成16

你知道HashMap的工作原理吗?” “你知道HashMap的get()方法的工作原理吗?
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。

当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。

基于Map接口实现、允许null键/值、非同步、不保证有序(比如插入的顺序)、也不保证序不随时间变化。HashMap存储着Entry(hash, key, value, next)对象。

当两个对象的hashcode相同会发生什么?”
因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对

如果两个键的hashcode相同,你如何获取值对象?
当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置
找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象

如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办
默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

HashMap中有一个成员变量modCount,这个用来实现“fast-fail”机制(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其结构性的修改,这是迭代器会立马感知到,并且立刻抛出ConcurrentModificationException异常,而不是等待迭代完成之后才告诉你已经出错。
与Hashtable的区别

HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable的key和value都不能为null,否则会报NullPointException)。

HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好

另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别

由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

HashMap不能保证随着时间的推移Map中的元素次序是不变的
HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap);
Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。

你知道get和put的原理吗?equals()和hashCode()的都有什么作用?
答:通过对key的hashCode()进行hashing,并计算下标( n-1 & hash),从而获得buckets的位置。如果产生碰撞,则利用key.equals()方法去链表或树中去查找对应的节点。

为什么String, Interger这样的wrapper类适合作为键?
String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用。因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。其他的wrapper类也有这个特点。
不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。如果你可以仅仅通过将某个field声明成final就能保证hashCode是不变的,那么请这么做吧。因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。

Java集合框架:ArrayList
ArrayList定义 构造ArrayList的时候,

默认初始化容量为10 扩容长度是旧长度的1.5倍+1(因为0的1.5还是0。确切的来说应该是:+1的1.5倍。)

每一次的扩容代表着创建新数组对象,复制原有数据。

package java.util;
publicclassArrayList extendsAbstractList
implementsList, RandomAccess, Cloneable, java.io.Serializable{privatestaticfinalint DEFAULT_CAPACITY = 10;
privatestaticfinal Object[] EMPTY_ELEMENTDATA = {};
private transient Object[] elementData;
privateint size;
//其余省略
}
ArrayList以数组实现,允许重复。超出限制时会增加50%的容量(grow()方法中实现,如下所示),每次扩容都底层采用System.arrayCopy()复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时创建数组的大小为10
按数组下标访问元素—get(i)/set(i,e) 的性能很高,这是数组的基本优势。
直接在数组末尾加入元素—add(e)的性能也高,但如果按下标插入、删除元素—add(i,e), remove(i), remove(e),则要用System.arraycopy()来移动部分受影响的元素,性能就变差了,这是基本劣势。
ArrayList中有一个方法trimToSize()用来缩小elementData数组的大小,这样可以节约内存:

考虑这样一种情形,当某个应用需要,一个ArrayList扩容到比如size=10000,之后经过一系列remove操作size=15,在后面的很长一段时间内这个ArrayList的size一直保持在<100以内,那么就造成了很大的空间浪费,这时候建议显式调用一下trimToSize()这个方法,以优化一下内存空间。  或者在一个ArrayList中的容量已经固定,但是由于之前每次扩容都扩充50%,所以有一定的空间浪费,可以调用trimToSize()消除这些空间上的浪费。
非线程安全,可以调用Collections.synchronizedList(new ArrayList<>());实现。
ArrayList是按插入顺序存储的,这也不奇怪,每次插入是在elementData[size++]处插入。

调用ArrayList中的subList方法生成的新的list,内部引用的还是原来的数组elementData,如果改变subList中的值,主list中的值也会跟着改变。

ArrayList和LinkedList的区别

ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

ArrayList和Vector的区别

1 Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
2 Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。
3 Vector还有一个子类Stack.

Java集合框架:Set(HashSet,LinkedHashSet,TreeSet)
Set概述 http://blog.csdn.net/u013256816/article/details/50917379

Set几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个Object。Set的特征也继承了那些内部Map实现的特征。

HashSet不允许重复(HashMap的key不允许重复,如果出现重复就覆盖),允许null值,非线程安全

Java集合框架:Collections工具类 http://blog.csdn.net/u013256816/article/details/50924875

0 0
原创粉丝点击