JAVA集合类图:
1. hashmap原理,与hashtable区别
更详细的原理请看: http://zhangshixi.iteye.com/blog/672697
区别:
http://blog.csdn.net/shohokuf/article/details/3932967
- HashMap允许键和值是null,而Hashtable不允许键或者值是null。
- Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
- HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败(具体看下文)的。另一方面,Hashtable提供了对键的列举(Enumeration)。
- 一般认为Hashtable是一个遗留的类。
2.让hashmap变成线程安全的两种方法
方法一:通过Collections.synchronizedMap()返回一个新的Map,这个新的map就是线程安全的. 这个要求大家习惯基于接口编程,因为返回的并不是HashMap,而是一个Map的实现.
方法二:使用ConcurrentHashMap
3.ArrayList也是非线程安全的
一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也将元素放在位置0,(因为size还未增长),完了之后,两个线程都是size++,结果size变成2,而只有 items[0]有元素。
util.concurrent包也提供了一个线程安全的ArrayList替代者CopyOnWriteArrayList。
4. hashset原理
5. ArrayList,Vector, LinkedList的存储性能和特性
ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
6.快速失败(fail-fast)和安全失败(fail-safe)
Fail-Fast机制:
我们知道Java.util.HashMap不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛ConcurrentModificationException,这就是所谓fail-fast策略。
这一策略在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。
- HashIterator() {
- expectedModCount = modCount;
- if (size > 0) { // advance to first entry
- Entry[] t = table;
- while (index < t.length && (next = t[index++]) == null)
- ;
- }
- }
在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map:
注意到modCount声明为volatile,保证线程之间修改的可见性。
- final Entry<K,V> nextEntry() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
在HashMap的API中指出:
由所有HashMap类的“collection 视图方法”所返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
Fail-Safe机制:Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败(一般的集合类)的,而java.util.concurrent包下面的所有的类(比如CopyOnWriteArrayList,ConcurrentHashMap )都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。
7.传递一个集合作为参数给函数时,我们如何能确保函数将无法对其进行修改
我们可以创建一个只读集合,使用Collections.unmodifiableCollection作为参数传递给使用它的方法,这将确保任何改变集合的操作将抛出UnsupportedOperationException。
8.Collections类的方法们
上面说到了很多了collections的方法,我们来深究一下这个类
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
1) 排序(Sort)
使用sort方法可以根据元素的自然顺序 对指定列表按升序进行排序。列表中的所有元素都必须实现 Comparable接口。此列表内的所有元素都必须是使用指定比较器可相互比较的
可以直接Collections.sort(...)
或者可以指定一个比较器,让这个列表遵照在比较器当中所设定的排序方式进行排序,这就提供了更大的灵活性
public static void sort(List l, Comparatorc)
这个Comparator同样是一个在java.util包中的接口。这个接口中有两个方法:int compare(T o1, T o2 )和boolean equals(Object obj)
2)很多常用的,没必要多讲的方法
shuffle(Collection) :对集合进行随机排序
binarySearch(Collection,Object)方法的使用(含义:查找指定集合中的元素,返回所查找元素的索引)
max(Collection),max(Collection,Comparator)方法的使用(前者采用Collection内含自然比较法,后者采用Comparator进行比较)
min(Collection),min(Collection,Comparator)方法的使用(前者采用Collection内含自然比较法,后者采用Comparator进行比较)。
indexOfSubList(List list,List subList)方法的使用(含义:查找subList在list中首次出现位置的索引)。
lastIndexOfSubList(List source,List target)方法的使用与上例方法的使用相同,在此就不做介绍了。
replaceAll(List list,Object old,Object new)方法的使用(含义:替换批定元素为某元素,若要替换的值存在刚返回true,反之返回false)。
以及等等等等。
3)我自己看看有哪些方法。 (这一段可以直接参考JAVA API说明 http://www.apihome.cn/api/java/Collections.html )
除了2)中讲到的一些零碎的,可以看到还分成了checked , empty , singleton, synchronized unmodifiable这几类。
checked:2个用途:
返回指定 collection 的一个动态类型安全视图。试图插入一个错误类型的元素将导致立即抛出 ClassCastException。假设在生成动态类型安全视图之前,collection 不包含任何类型不正确的元素,并且所有对该 collection 的后续访问都通过该视图进行,则可以保证 该 collection 不包含类型不正确的元素。
一般的编程语言机制中都提供了编译时(静态)类型检查,但是一些未经检查的强制转换可能会使此机制无效。通常这不是一个问题,因为编译器会在所有这类未经检查的操作上发出警告。但有的时候,只进行单独的静态类型检查并不够。例如,假设将 collection 传递给一个第三方库,则库代码不能通过插入一个错误类型的元素来毁坏 collection。
动态类型安全视图的另一个用途是调试。假设某个程序运行失败并抛出 ClassCastException,这指示一个类型不正确的元素被放入已参数化 collection 中。不幸的是,该异常可以发生在插入错误元素之后的任何时间,因此,这通常只能提供很少或无法提供任何关于问题真正来源的信息。如果问题是可再现的,那么可以暂时修改程序,使用一个动态类型安全视图来包装该 collection,通过这种方式可快速确定问题的来源。
unmodifiable:
返回指定 集合的不可修改视图。此方法允许模块为用户提供对内部 集合的“只读”访问。在返回的 集合 上执行的查询操作将“读完”指定的集合。试图修改返回的集合(不管是直接修改还是通过其迭代器进行修改)将导致抛出 UnsupportedOperationException。
synchronized:
public static <T> Collection<T> synchronizedCollection(Collection<T> c)
-
返回指定 collection 支持的同步(线程安全的)collection。为了保证按顺序访问,必须通过返回的 collection 完成
所有对底层实现 collection 的访问。
在返回的 collection 上进行迭代时,用户必须手工在返回的 collection 上进行同步:
Collection c = Collections.synchronizedCollection(myCollection); ... synchronized(c) { Iterator i = c.iterator(); // Must be in the synchronized block while (i.hasNext()) foo(i.next()); }
empty: (以set 为例,我没看懂到底是干嘛的。。)
public static final <T> Set<T> emptySet()
-
返回空的 set(不可变的)。此 set 是可序列化的。与
like-named(找不到关于这个东西的资料。。) 字段不同,此方法是参数化的。
以下示例演示了获得空 set 的类型安全 (type-safe) 方式:
Set<String> s = Collections.emptySet();
9.Tree, Hash ,Linked
再看看这个图。
发现set和map的实现分成了 Tree,Hash,和Linked。
以map为例,来看看这三者的区别.
TreeMap用红黑树实现,能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。当用Iteraor遍历TreeMap时,得到的记录是排过序的。TreeMap的键和值都不能为空。
HashMap上文有说。
LinkedHashmap:它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。put方法没有重写,重写了addEntry()。(因为加入的时候要维护好一个双向链表的结构)LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时,记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除。由于的链表的增加、删除操作是常量级的,故并不会带来性能的损失(accessOrder是LinkedHashmap中的一个属性,用来判断是否要根据读取顺序来重写调整结构。如果为false,就按照插入的顺序排序,否则按照最新访问的放在链表前面的顺序,以提高性能)。
我个人的理解是:LinedHashMap的作用就是在让经常访问的元素更快的被访问到。用双向链表可以方便地执行链表中元素的插入删除操作。