集合简单介绍
Map接口和Collection接口是所有集合框架的父接口。
Collection接口
java中没有提供Collection接口的实现,而是让List接口和set接口去继承它。
public interface List<E> extends Collection<E>
List接口的实现类有:ArrayList、LinkedList、Vector。
public interface Set<E> extends Collection<E>
set接口的实现类有:HashSet、TreeSet、LinkedHashSet。
Map接口
Map接口的实现类有:HashMap、LinkedHashMap、HashTable、TreeMap.
List:有序,可以重复的集合。
List 接口的三个典型实现:
①、List list1 = new ArrayList();
底层数据结构是数组,查询快,增删慢;线程不安全,效率高。初始大小为10,每次扩容为原来的0.5倍。
②、List list2 = new Vector();
底层数据结构是数组,查询快,增删慢;线程安全,效率低,几乎已经淘汰了这个集合。每次扩容为原来的1倍。
③、List list3 = new LinkedList();
底层数据结构是链表,查询慢,增删快;线程不安全,效率高
Set:无序,不可重复的集合。
1、Set hashSet = new HashSet();
①、HashSet:不能保证元素的顺序;不可重复;不是线程安全的;集合元素可以为 NULL;
②、其底层其实是一个数组,存在的意义是加快查询速度。我们知道在一般的数组中,元素在数组中的索引位置是随机的,元素的取值和元素的位置之间不存在确定的关系,因此,在数组中查找特定的值时,需要把查找值和一系列的元素进行比较,此时的查询效率依赖于查找过程中比较的次数。而 HashSet 集合底层数组的索引和值有一个确定的关系:index=hash(value),那么只需要调用这个公式,就能快速的找到元素或者索引。
③、对于 HashSet: 如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。
1、当向HashSet集合中存入一个元素时,HashSet会先调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置
1.1、如果 hashCode 值不同,直接把该元素存储到 hashCode() 指定的位置
1.2、如果 hashCode 值相同,那么会继续判断该元素和集合对象的 equals() 作比较
1.2.1、hashCode 相同,equals 为 true,则视为同一个对象,不保存在 hashSet()中
1.2.2、hashCode 相同,equals 为 false,则存储在之前对象同槽位的链表上,这非常麻烦,我们应该约束这种情况,即保证:如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。
2、Set linkedHashSet = new LinkedHashSet();
不可以重复,有序。因为底层采用 链表 和 哈希表的算法。链表保证元素的添加顺序,哈希表保证元素的唯一性。
3、Set treeSet = new TreeSet();
有序;不可重复,底层使用 红黑树算法,擅长于范围查询。需实现Comparable或compareTo接口。
以上三个 Set 接口的实现类比较:
共同点:
1、都不允许元素重复
2、都不是线程安全的类,解决办法:Set set = Collections.synchronizedSet(set 对象)
不同点:
HashSet:不保证元素的添加顺序,底层采用 哈希表算法,查询效率高。判断两个元素是否相等,equals() 方法返回 true,hashCode() 值相等。即要求存入 HashSet 中的元素要覆盖 equals() 方法和 hashCode()方法
LinkedHashSet:HashSet 的子类,底层采用了 哈希表算法以及 链表算法,既保证了元素的添加顺序,也保证了查询效率。但是整体性能要低于 HashSet
TreeSet:不保证元素的添加顺序,但是会对集合中的元素进行排序。底层采用 红-黑 树算法(树结构比较适合范围查询)
注意:尽量使用迭代器对非线程安全的集合进行遍历。
原因:在使用迭代器的过程中有其他线程修改了集合,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对集合内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了 集合。
使用场景:
(此图来源于https://blog.csdn.net/feiyanaffection/article/details/81394745)
Map:key-value 的键值对,key 不允许重复,value 可以
严格来说 Map 并不是一个集合,而是两个集合之间 的映射关系。
这两个集合没每一条数据通过映射关系,我们可以看成是一条数据。即 Entry(key,value)。Map 可以看成是由多个 Entry 组成。
因为 Map 集合即没有实现于 Collection 接口,也没有实现 Iterable 接口,所以不能对 Map 集合进行 for-each 遍历。
遍历实例:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("first", "linlin");
map.put("second", "好好学java");
map.put("third", "sihai");
map.put("first", "sihai2");
// 第一种:通过Map.keySet遍历key和value
System.out.println("===================通过Map.keySet遍历key和value:===================");
for (String key : map.keySet()) {
System.out.println("key= " + key + " and value= " + map.get(key));
}
// 第二种:通过Map.entrySet使用iterator遍历key和value
System.out.println("===================通过Map.entrySet使用iterator遍历key和value:===================");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
// 第三种:通过Map.entrySet遍历key和value
System.out.println("===================通过Map.entrySet遍历key和value:===================");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
// 第四种:通过Map.values()遍历所有的value,但是不能遍历键key
System.out.println("===================通过Map.values()遍历所有的value:===================");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
1、HashMap hashMap = new HashMap();
key的值可以为null,但只有一个key为null;它是线程不安全的。
底层实现
HashMap以键值对(key-value)的形式来存储元素,在调用Put方法时,HashMap会通过hash函数来计算key的hash值,然后通过hash值来判断存储位置是否已存在元素,如果存在的话,就判断要存储的元素与当前位置元素的key是否相同,如果相同则覆盖,如果不同的话就会发送哈希冲突,将冲突的值加到链表中即可。Jdk1.8后,当链表的长度大于8时,链表转为红黑树。
红黑树
- 节点是红色或者黑色
- 根节点都是黑色
- 每个叶子的节点都是黑色的空节点
- 每个红色节点的两个子节点都是黑色的
- 从任意节点到其每个叶子的所有路径都包含相同的黑色节点
(图片来源https://www.cnblogs.com/linliquan/p/11323172.html)
2、LinkedHashMap linkedHashMap= new LinkedHashMap();
有序的,使用链表来扩展HashMap。
3、HashTable hashTable= new HashTable();
相比HashMap,它是线程安全的。除此之外,key和value的值都不能为null。
4、TreeMap treeMap= new TreeMap();
基于红黑树数据结构实现,键值使用compareTo或comparable接口来排序。
此文章借鉴的博客有:
https://www.cnblogs.com/ysocean/p/6555373.html
https://blog.csdn.net/feiyanaffection/article/details/81394745
https://www.cnblogs.com/linliquan/p/11323172.html