“靡不有初,鲜克有终”,读音是“mǐ bù yǒu chū,xiǎn kè yǒu zhōng”,原意是凡事都有个开始,但经常不了了之,没个结果。后借此语以讽谕持志不终的人 ,用以告诫人们为人做事要善始善终。
今天也是要好好学习的一天,你学习了吗
Map 接口
在 Collection 集合中,每次操作的都是一个对象,如果现在假设要操作一对对象,比如,甲喜欢上了丙,但没有联系方法,但乙认识丙,乙和丙不是同一对象,但是通过乙可以找到丙,这就是映射关系,这种就必须使用 Map 了,类似于以下一种情况:
姓名 | 身份证号 |
---|---|
张三 | 360278123456… |
李四 | 360678234567… |
由于业务需求,名字和身份证号要分开存放,身份证号的唯一性和键(key)相得益彰,将身份证号存放在key中,必然可以通过唯一的键找到对应的值,不同的键可以有相同的值(可以有很多个张三(value),但身份证号(key)唯一),Map 接口里面的所有内容都按照 key -> value 的形式保存,键值对,也称为二元偶对象。
此接口定义如下:
Interface Map<K,V>
-
参数类型
K – 此映射维护的密钥类型
V – 映射值的类型 (K,V泛型类型,是操作者指定的类型)
Interface Map.Entry<K,V> (Map的内部接口)键值对就是entry。 Map.entrySet方法返回 Map的集合视图,其元素属于此类
此接口与 Collection 接口没有任何的关系(并列的顶级集合接口,一个单值,一个键值对),是第二大的集合操作接口。
此接口常用方法如下:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | void clear() | 普通 | 清空 Map 集合中的内容 |
2 | boolean containsKey(Object key) | 普通 | 判断集合中是否存在指定的 key |
3 | boolean containsValue(Object value) | 普通 | 判断集合中是否存在指定的 value |
4 | Set<Map.Entry<K,V>> entrySet() | 普通 | 将 Map 接口变为 Set 集合 |
5 | V get(Object key) | 普通 | 根据 key 到其对应的 value |
6 | boolean isEmpty() | 普通 | 判断是否为空 |
7 | Set keySet() | 普通 | 将全部的 key 变为 Set 集合 |
8 | Collection values() | 普通 | 将全部的 value 变为 Collection 集合 |
9 | V put(K key,V value) | 普通 | 向集合中增加内容 |
10 | void putAll(Map<? extends K,? extends V> m) | 普通 | 增加一组集合 |
11 | V remove(Object key) | 普通 | 根据 key 删除内容 |
Map接口提供了三个collection-view(collection集合视图) ,这些视图允许将映射的内容视为一组键集合(类型:Set ,方法: keySet()),值集合(类型:Collection , 方法:values())或键值映射集(类型:Set<Map.Entry<K,V>> , 方法 entrySet())。
Map的顺序被定义为其中在Map上的collection-view迭代返回元素的顺序。 一些Map实现,如TreeMap类,对其顺序做出了具体保证; 其他的,比如HashMap集合,没有。
注意:如果将可变对象用作映射键,则必须非常小心(最好不要将可变对象用作键值,后果很严重)。 如果在对象是Map中的键时,以影响equals比较的方式更改对象的值,则不指定映射的行为。 这种禁令的一个特例是,Map不允许将自己作为一个关键词。 虽然允许地图将自身包含为值,但建议极为谨慎: equals和hashCode方法在此类Map上不再明确定义。
Map 本身是一个接口,所以一般会使用以下的几个子类:HashMap、TreeMap、Hashtable。
HashMap集合
HashMap 是 Map 的子类,此类的定义如下:
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
(此类继承了 AbstractMap 类,同时可以被克隆,可以被序列化下来。)
基于哈希表的Map接口的实现。此实现提供了所有可选的映射操作,并允许null值和null键。( HashMap类大致相当于Hashtable ,除了它是不同步的并且允许空值。)此类不保证元素的顺序;特别是它不保证元素会随着时间的推移保持不变(当其容量不足时会重建内部数据结构,即集合会自动重新散列来扩展表)。
该实现为基本操作( get和put )提供了恒定时间性能,假设散列函数在桶之间正确地分散元素。 对集合视图的迭代需要与HashMap实例的“容量”(桶数)加上其大小(键值映射的数量)成比例的时间。 因此,如果迭代性能很重要,则不要将初始容量设置得太高(或负载因子太低)非常重要。
HashMap的实例有两个影响其性能的参数: 初始容量和负载因子。 容量是哈希表中的桶数,初始容量只是创建哈希表时的容量。 加载因子是在自动增加容量之前允许哈希表获取的完整程度的度量。 当哈希表中的桶使用数超过加载因子和当前容量的乘积时,哈希表将被重新哈希(即重建内部数据结构),以便哈希表具有大约两倍的桶数。
作为一般规则,默认加载因子(0.75)在时间和空间成本之间提供了良好的折衷。 较高的值会减少空间开销,但会增加查找成本(反映在HashMap类的大多数操作中,包括get和put )。 在设置其初始容量时,应考虑映射中的预期条目数及其负载因子,以便最小化重新散列操作的数量。 如果初始容量大于最大条目数除以加载因子,则不会发生重新加载操作。
如果要将多个映射存储在HashMap实例中,则使用足够大的容量创建映射将允许映射更有效地存储,而不是根据需要执行自动重新散列来扩展表。 请注意,使用具有许多相同hashCode()的键是减慢任何哈希表性能的可靠方法(即想要提高性能就不要有多个相同hashCode()的键)。 为了改善影响,当键为Comparable时,此类可以使用键之间的比较顺序来帮助打破关系。
构造器
构造器 | 描述 |
---|---|
HashMap() | 使用默认初始容量(16)和默认加载因子(0.75)构造一个空 HashMap 。 |
HashMap(int initialCapacity) | 使用指定的初始容量和默认加载因子(0.75)构造一个空 HashMap 。 |
HashMap(int initialCapacity, float loadFactor) | 使用指定的初始容量和加载因子构造一个空 HashMap 。 |
HashMap(Map<? extends K,? extends V> m) | 构造一个新的 HashMap ,其映射与指定的 Map相同。 |
方法摘要 (标红注释是我的理解)
变量和类型 | 方法 | 描述 |
---|---|---|
void | clear() | 从此映射中删除所有映射。(清空) |
Object | clone() | 返回此 HashMap实例的浅表副本:未克隆键和值本身。 |
V | compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 尝试计算指定键及其当前映射值的映射(如果没有当前映射, null )。 |
V | computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) | 如果指定的键尚未与值关联(或映射到 null ),则尝试使用给定的映射函数计算其值并将其输入此映射,除非 null 。 |
V | computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 如果指定键的值存在且为非null,则尝试在给定键及其当前映射值的情况下计算新映射。 |
boolean | containsKey(Object key) | 如果此映射包含指定键的映射,则返回 true 。 (是否包含某键) |
boolean | containsValue(Object value) | 如果此映射将一个或多个键映射到指定值,则返回 true 。 (是否包含某值) |
Set<Map.Entry<K,V>> | entrySet() | 返回此映射中包含的映射的Set视图。 (返回一个set集合) |
V | get(Object key) | 返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。(根据键取值) |
boolean | isEmpty() | 如果此映射不包含键 - 值映射,则返回 true 。 (是否为空) |
Set< K > | keySet() | 返回此映射中包含的键的Set视图。 (调用此方法得到一个关于key的Set集合) |
V | merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) | 如果指定的键尚未与值关联或与null关联,则将其与给定的非空值关联。 |
V | put(K key, V value) | 将指定的值与此映射中的指定键相关联。(存入单个键值对) |
void | putAll(Map<? extends K,? extends V> m) | 将指定映射中的所有映射复制到此映射。(存入整个m集合,相当于存入多个键值对) |
V | remove(Object key) | 从此映射中删除指定键的映射(如果存在)。 (删除键值对) |
int | size() | 返回此映射中键 - 值映射的数量。 (集合实际长度,不是容量) |
Collection< V > | values() (比较少用) | 返回此映射中包含的值的Collection视图。 (返回一个Collection集合) |
HashMap集合底层存储数据的结构是哈希表
什么是哈希表呢?
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。当其长度减小到6时又变回链表。(所以长度为7时是个坑,可能是链表也可能是红黑树。)
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能。
Hashtable 集合
定义:public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, Serializable
该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或值。
要成功存储和检索哈希表中的对象,用作键的对象必须实现hashCode方法和equals方法。
此示例创建数字哈希表。 它使用数字的名称作为键:
Hashtable<String, Integer> numbers = new Hashtable<String, Integer>();
numbers.put("one", 1);
numbers.put("two", 2);
numbers.put("three", 3);
要检索数字,请使用以下代码:
Integer n = numbers.get("two");
if (n != null) {
System.out.println("two = " + n);
}
从Java 2平台1.2版本开始,该类被改进以实现Map接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Hashtable是同步的。 如果不需要线程安全实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的高度并发实现,则建议使用ConcurrentHashMap代替Hashtable 。
构造器
构造器 | 描述 |
---|---|
Hashtable() | 使用默认初始容量(11)和加载因子(0.75)构造一个新的空哈希表。 |
Hashtable(int initialCapacity) | 使用指定的初始容量和默认加载因子(0.75)构造一个新的空哈希表。 |
Hashtable(int initialCapacity, float loadFactor) | 使用指定的初始容量和指定的加载因子构造一个新的空哈希表。 |
Hashtable(Map<? extends K,? extends V> t) | 构造一个新的哈希表,其具有与给定Map相同的映射。 |
方法
两者操作方法基本一样(map各子类集合的操作方法都差不多,同理其他集合也是)
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class Demo {
//HashMap/Hashtable/ConcurrentHashMap
//TreeMap
//LinkedHashMap
public static void main(String[] args) {
//key 和 value 可以是自定义类型,key值建议不要随意更改
Map<String, String> data = new TreeMap<>();
data.put("key1", "赵钱孙李");//增加方法
data.put("key2", "周吴郑王");
data.put("key3", "1周吴郑王");
data.put("key4", "小明同学");
//boolean remove = data.remove("key4", "小明同");//false
data.remove("key4");//删除方法
int size = data.size();//集合内键值对数量
System.out.println("集合内键值对数量:" + size);
boolean empty = data.isEmpty();//集合是否为空
System.out.println("集合是否为空:" + empty);
data.put("", "");
boolean key1 = data.containsKey("key1");//是否包含某个键,containsValue是否包含某个值
System.out.println("集合是否包含key1:" + key1);
//data.clear();清空集合
//data.put(null, null);//只有HashMap和LinkedHashMap可以存储,其余会报空指针异常
//data.put(null, null);//key不可重复,相同新value覆盖旧value
System.out.println(data);
System.out.println("-----------以上直接输出整个集合------------");
/*String value = data.get("key1");
System.out.println(value);
value = data.get("key2");//根据键取值
System.out.println(value);*/
Set<String> set = data.keySet();
for (String key :
set) {
System.out.println(key + "->" + data.get(key));
}
System.out.println("------以上将key转为Set集合遍历键调用get(key)方法取值--------");
Collection<String> values = data.values();//把所有值转成Collection集合
for (String value :
values) {
System.out.println(value);
}
System.out.println("---------将value转为Collection集合遍历值(无法得到key)--------------");
for (Map.Entry<String, String> entry : data.entrySet()) {
System.out.println(entry.getKey() + "->" + entry.getValue());
}
System.out.println("---------将entry转为Set集合遍历键值对--------------");
Iterator<Map.Entry<String, String>> iterator = data.entrySet().iterator();
//Iterator<String> iterator = data.keySet().iterator();
//Iterator<String> iterator = data.values().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
System.out.println(next);
}
System.out.println("-------------还能用iterator迭代器输出---------------");
}
}
HashMap 与 Hashtable 的区别
在整个集合中除了 ArrayList 和 Vector 的区别之外,另外一个最重要的区别就是 HashMap 与 Hashtable 的区别。
No. | 区别点 | HashMap | Hastable |
---|---|---|---|
1 | 推出时间 | JDK 1.2 之后推出的,新的操作类 | JDK 1.0 时推出的,旧的操作类 |
2 | 性能 | 异步处理,性能较高 | 同步处理,性能较低 |
3 | null | 允许设置为 null | 不允许设置,否则将出现空指向异常 |
4 | 迭代 | 快速失败 | 快速失败 |
5 | 初始容量(桶数) | 16 | 11 |
6 | 负载因子 | 0.75 | 0.75 |
循环方式上面代码里有示例。