此图来源于:http://blog.csdn.net/u010887744/article/details/50575735
大图可以点此访问:https://img-blog.csdn.net/20160124221843905
Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。
**Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类。
最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。
List
List接口是一个有序(插入输出的顺序一致,并不是说按大小排序)**的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
list实现类有:ArrayList/Vector/LinkedList,都是可伸缩数组。
其中ArrayList/Vector是基于Object[] array实现的,使用连续的空间,支持用序号访问元素。其中Vector是线程安全的(Vector 的所有方法加上了 synchronized 关键字),ArrayList是线程不安全的。**java中Stack是Vector的一个子类。**ArrayList/Vector都是查询快但是插入慢。Vector属于遗留容器,不再建议使用。(除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器)。
LinkedList采用双向链表实现。非线程安全,插入块,查询慢
Set
Set是一个接口,只存储不相同的数据。Set都是基于Map的,即将数据存储在Map的key中,将value置为null。
Set实现类有:HashSet/TreeSet
HashSet判断是否有重复数据: HashSet 的 add() 方法添加集合元素时实际上转变为调用 HashMap 的 put() 方法来添加 key-value 对。故其实是调用调用hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。
TreeSet判断是否有重复数据:TreeSet 的 add() 方法添加集合元素时实际上转变为TreeMap中调用put方法添加键值对,即调用compareTo对所有键进行比较。
Queue
Queue接口扩展了java.util.Collection接口,但是Queue使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素,它们的优点是通过返回值可以判断成功与否,而add()和remove()方法在失败的时候会抛出异常。peek()可以使用队列头部元素而不移出。
Queue的实现类有:ArrayDeque/PriorityQueue/LinkedList
值得注意的是LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。(LinkedList也是List)
**PriorityQueue:**底层是最小堆,所以要使用最小堆的时候可以用PriorityQueue,用最大堆的时候,可以改变PriorityQueue。
PriorityQueue maxHeap = new PriorityQueue(11,new Comparator() {
@Override //用PriorityQueue实现大根堆只需要重写compare,11是默认大小
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2.compareTo(o1);
}
});
Java中Comparable和Comparator的用法
https://www.cnblogs.com/skywang12345/p/3324788.html
Deque接口:Deque接口是Queue接口的子接口,代表一个双端队列。同时Deque不仅可以作为双端队列使用,而且可以被当成栈来使用,所以可以使用出栈,入栈的方法。其实现类为ArrayDeque。现在已经不建议用Stack做栈,而建议用ArrayDeque做栈。
LinkedList实现类
LinkedList类是List接口的实现类,但是同时也是Deque接口实现类,所以LinkedList既可以当做双端队列来使用,也可以当做栈来使用
Map
Map 接口存储一组键值对象,提供key(键)到value(值)的映射。
实现类有HashMap/HashTable/TreeMap/WeakHashMap
hashmap是hashtable的轻量级实现;
hashmap和hashtable的区别:
1、hashmap线程不安全,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null;
hashtable线程安全,不允许允许null作为key和value值;
2、HashMap的迭代器(Iterator)是fail-fast迭代器(快速失败),而Hashtable的enumerator迭代器
3、HashMap的默认大小是16,扩容方法是old2,故hashmap的存储数组一直是2的指数,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
Hashtable默认数组大小事11,的扩容方法是old2+1
4、HashMap把Hashtable的contains方法去掉了,变成了containsvalue和containsKey。
hashmap和hashtable的相同点:
1、hashmap和hashtable都不允许有重复的键(key值),即,每个键只能映射一个值。
2、都是用数组链表存储数据的(但是hashmap在jdk1.8后,当链表长度超过阈值时(默认为8),用红黑树代替链表)
数组链表存储的思路:
https://www.cnblogs.com/aspirant/p/8908399.html
即数组中和链表节点中存储的是hashmap内部定义的一个类Entry,
Entry包含三个元素:key,value和指向下一个Entry的next。
插入元素时(例如插入x),先hashcode(x),看数组中是否有hash值相等的情况,没有的话直接插入,有hash值相同的情况,则用equal()方法遍历链表,看是否有key相同的情况,若key相同,则不能插入,如果没有key相同的情况,则插入一个新的Entry。
hashmap扩容机制
HashMap默认使用的负载因子值为0.75f(当容量达到四分之三进行再散列(扩容))。当负载因子越大的时候能够容纳的键值对就越多但是查找的代价也会越高。
jdk1.8之前扩容的时候要重新计算所有元素的位置,即resize();
jdk1.8当链表长度大于8时,用红黑树代替链表。
LinkedHashMap是HashMap的一个子类 HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。这个时候,LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序。
hashtable和ConcurrentHashMap都是线程相同的,ConcurrentHashMap用来替代hashtable。
hashtable和ConcurrentHashMap的区别:
hahstable实现线程安全的方式是在修改数据时锁住整个HashTable,效率低;
ConcurrentHashMap每次锁住一个segment,只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
ConcurrentHashMap的概念:
1、底层采用分段的数组+链表实现,线程安全
2、使用锁分段技术确保线程安全
锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
目前,hashtable已经不常用了,如果想要线程安全,可以用ConcurrentHashMap;
TreeMap实现了SortMap接口,所以键值对按照顺序存放。
TreeMap是通过红黑树实现的
WeakHashMap和HashMap类似,不同之处在于WeakHashMap的Key采用“弱引用”,即WeakHashMap中的key不被外部引用时,就可以被垃圾回收器回收,而HashMap中的key是强引用,只有将key删除了,才能被垃圾回收器回收。
Map.Entry
Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry
Map map = new HashMap();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
代码转自https://blog.csdn.net/yaomingyang/article/details/78748130