Java中的集合类有两个重要的分支,分别是接口Collection(包括List,Set等)和接口Map。
由于HashSet的内部实现原理使用了HashMap,所以我们先来了解Map集合类。
1.HashMap、Hashtable和TreeMap
(1)java.lang.Object
继承者 java.util.AbstractMap<K,V>
继承者 java.util.HashMap<K,V>
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
(2)java.lang.Object
继承者 java.util.Dictionary<K,V>
继承者 java.util.Hashtable<K,V>
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable
(3)java.lang.Object
继承者 java.util.AbstractMap<K,V>
继承者 java.util.TreeMap<K,V>
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
其中,HashMap不是线程
安全的;HashTable是线程安全的,其线程安全是通过sychronize实现的。基于该原因,HashMap效率高于HashTable。
HashMap的键可以为null,HashTable不可以。
在多线程环境下,HashMap配合Collections工具类使用来实现线程安全;还可以选择ConcurrentHashMap,该类的线程安全是通过Lock的方式实现的,所以效率高于Hashtable。
数组,链表,哈希表,三者各有优劣。数组使用连续的内存空间,查找速度快,增删慢;链表充分利用了内存,存储空间是不连续的,首尾各存储上下一个节点的信息,所以寻址较慢,即查找速度慢,但是增删快;哈希表综合了前二者的优点,一个哈希表,由数组和链表组成。假设一条链表有1000个节点,现在要查找最后一个节点,就得从第一个遍历到最后一个;如果用哈希表,将这条链表分为10组,用一个容量为10的数组来存储这10组链表的头结点(a[0] = 0 , a[1] = 100 , a[2] = 200 …以此类推),这样的话就明显提高了寻址效率。
HashMap就是基于上述哈希表原理实现的寻址,Hashtable同理,只不过做了同步处理。
HashMap输出是无序的,这个无序不是说每次遍历的结果顺序不一样,而是说与插入顺序不一样
另外,TreeMap是按键排序的,默认升序,所以可以通过TreeMap来实现。TreeMap的排序是在底层基于比较器(Comparator接口的int compare(T o1,T o2),Comaparable接口的int compareTo(T o))实现的。
HashMap实现线程同步的方式有两种,一种是:
Map<Integer , String> hs = new HashMap<Integer , String>(); hs = Collections.synchronizedMap(hs);
另一种是:
ConcurrentHashMap<Integer , String> hs = new ConcurrentHashMap<Integer , String>();
2.List接口及其子类ArrayList,LinkedList,Vector,Stack。
public interface List<E> extends Collection<E>
(1)java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractList<E>
继承者 java.util.ArrayList<E>
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
(2)java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractList<E>
继承者 java.util.AbstractSequentialList<E>
继承者 java.util.LinkedList<E>
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
(3)java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractList<E>
继承者 java.util.Vector<E>
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
(4)java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractList<E>
继承者 java.util.Vector<E>
继承者 java.util.Stack<E>
public class Stack<E> extends Vector<E>
其中,ArrayList和Vector本质都是用数组实现的,而LinkList是用双链表实现的。所以,Arraylist和Vector在查找效率上比较高,增删效率比较低;LinkedList则正好相反。
ArrayList是线程不安全的,Vector是线程安全的,在效率没有ArrayList高。实际使用中,一般也不怎么用Vector,可以自己做线程同步,也可以用Collections配合ArrayList实现线程同步。
在排序上,List是使用Collections类的sort方法,构造Comparator或者让List中的对象实现Comparable都可以。
Stack继承自Vector,在用法、线程安全方面的跟Vector都差不多,有几个地方需要注意,一是add()和push(),stack是将最后一个element作为栈顶的,add()返回boolean,就是添加成功了没有,push()返回的是添加的元素,一般推荐使用push;二是peek()和pop(),这两个方法都能得到栈顶元素,区别是peek()只是读取,对原栈没有什么影响,pop()从字面上就能理解为“出栈”,所以原栈的栈顶元素就没了。
3.Set接口及其子类HashSet,TreeSet。
public interface Set<E> extends Collection<E>
(1)java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractSet<E>
继承者 java.util.HashSet<E>
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
(2)java.lang.Object
继承者 java.util.AbstractCollection<E>
继承者 java.util.AbstractSet<E>
继承者 java.util.TreeSet<E>
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
总的来说,Set集合类的特点是可以去重,它们的内部实现都是基于Map的,用的是Map的key,而Map的key是不可以重复的。既然要去重,就需要比较,而比较是基于hascode()方法和equals()方法的。
原文地址:https://www.cnblogs.com/lizhangyong/p/8150975.html