Map( 双列集合框架 )
1 、 Map 接口及实现类概述
Map 接口提供三种 collection 视图,允许以键集、值集或键 - 值映射关系集的形式查看某个映射的内容。映射顺序
定义为迭代器在映射的collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap
类;另一些映射实现则不保证顺序,如 HashMap 类。
注:将可变对象用作映射键时必须格外小心。当对象是映射中某个键时,如果以影响 equals
比较的方式更改了对象的值,则映射的行为将是不确定的。此项禁止的一种特殊情况是不允许某个映射将自身作为一个键包含。虽然允许某个映射将自身作为值包含,但请格外小心:在这样的映射上
equals 和 hashCode 方法的定义将不再是明确的。
所有通用的映射实现类应该提供两个“标准的”构造方法:一个 void (无参数)构造方法,用于创建空映射;一个是带有单个 Map
类型参数的构造方法,用于创建一个与其参数具有相同键 -
值映射关系的新映射。实际上,后一个构造方法允许用户复制任意映射,生成所需类的一个等价映射。尽管无法强制执行此建议(因为接口不能包含构造方法),但是
JDK 中所有通用的映射实现都遵从它。
此接口中包含的“破坏”方法可修改其操作的映射,如果此映射不支持该操作,这些方法将抛出
UnsupportedOperationException 。如果是这样,那么在调用对映射无效时,这些方法可以(但不要求)抛出
外汇返佣http://www.kaifx.cn/ UnsupportedOperationException
。例如,如果某个不可修改的映射(其映射关系是“重叠”的)为空,则对该映射调用 putAll(Map)
方法时,可以(但不要求)抛出异常。
某些映射实现对可能包含的键和值有所限制。例如,某些实现禁止 null
键和值,另一些则对其键的类型有限制。尝试插入不合格的键或值将抛出一个未经检查的异常,通常是 NullPointerException
或 ClassCastException 。试图查询是否存在不合格的键或值可能抛出异常,或者返回false
;某些实现将表现出前一种行为,而另一些则表现后一种。一般来说,试图对不合格的键或值执行操作且该操作的完成不会导致不合格的元素被插入映射中时,将可能抛出一个异常,也可能操作成功,这取决于实现本身。这样的异常在此接口的规范中标记为“可选”。
此接口是 Java Collections Framework 的成员。
Collections Framework 接口中的很多方法是根据 equals 方法定义的。例如,
containsKey(Object key) 方法的规范中写道:“当且仅当此映射包含针对满足 (key == null ?
k==null : key.equals(k)) 的键 k 的映射关系时,返回 true ”。不 应将此规范解释为:调用具有非空参数
key 的Map.containsKey 将导致对任意的键 k 调用 key.equals(k) 。实现可随意进行优化,以避免调用
equals ,例如,可首先比较两个键的哈希码(Object.hashCode()
规范保证哈希码不相等的两个对象不会相等)。一般来说,只要实现者认为合适,各种 Collections Framework
接口的实现可随意利用底层 Object 方法的指定行为。
|---Map 接口:存储的是一对一对的数据: key-value
|----HashMap: 主要实现类;线程不安全的,效率高;允许添加 null 的 key 或 null 的
value
|----LinkedHashMap :是 HashMap
的子类,可以实现照添加的顺序实现遍历。(因为使用了一对双向链表记录添加元素的先后顺序),对于频繁的遍历操作,建议使用此类。
|----TreeMap :可以照元素 key 的指定的属性的大小实现遍历。底层使用红黑树实现。
|----Hashtable :古老实现类;线程安全的,效率低;不允许添加 null 的 key 或 null 的
value
|----Properties: 常用来处理属性文件。 key 和 value 都是 String 类型。
2 、关于对Map 的理解
Map 中的 key 是无序的、不可重复的。 key 构成的集合是 Set ---> 需要重写必要的 hashCode()
和 equals() 。
Map 中的 value 是无序的、可重复的。 value 构成的集合是 Collection ---> 需要重写必要的
equals() 。
Map 中的一个键值对构成一个 entry 。
Map 中的 entry 是无序的、不可重复的。 entry 构成的集合是 Set 。
3 、常用方法
增:put(Object key,Object value)
删:remove(Object key)
改:put(Object key,Object value)
查:get(Object key)
长度:size()
遍历:keySet() 、 values() 、 entrySet()
4 、内部结构实现原理****
很重要,必须要掌握。
4 .1 HashMap 在 jdk7 中实现原理:
HashMap map = new HashMap();//Entry[] table = new Entry;
...
map.put(key1,value1);// key1,value1 会封装在一个 entry 对象中。将此对象存放到
table 数组中
将 key1,value1 添加到 table 中,首先根据 key1 所在类的 hashCode() 方法,计算 key1
的哈希值 1 ,然后使用某种算法,得到哈希值 2 。 哈希值 2 再使用 indexFor() 方法得到其在底层 table
数组中的存放位置: index 。( 0<= index <= 15 )
-> 如果 index 位置上没元素,则 key1,value1 直接添加成功。 ---- 添加成功操作 1
-> 如果 index 位置上元素 (key2,value2) (或已经存在一个链表结构) , 则比较 key1 和
key2 (或链表上每个 key 的哈希值 .
-> 如果二者 ( 或比较多个 key 以后 ) 的哈希值不同,则 (key1,value1) 添加成功。 ----
添加成功操作 2
-> 如果二者的哈希值相同,则调用 key1 所在类的 equals() ,将 key2 作为参数传递到此方法中,比较两个
key是否 equals
-> 如果返回值为 false: 则 (key1,value1) 添加成功。 ---- 添加成功操作 3
-> 如果返回值为 true: 则 value1 替换原 key2 对应的 value2. ---- 修改成功
说明:添加成功操作1 :将 key1,value1 存放到数组的位置上。
添加成功操作 2 ,添加成功操作 3 :将 key1,value1 添加到链表上。存放到链表头部
扩容的情况:当添加的数据达到临界值(= 数组的长度 * 加载因子)时,则进行扩容。默认的临界值为: 16 * 0.75 = 12
。默认扩容为原来的 2 倍。
数组的长度:DEFAULT_INITIAL_CAPACITY : 16
加载因子:DEFAULT_LOAD_FACTOR : 0.75
threshold :临界值 = 数组的长度 capacity * 加载因子 loadFactor 。
4 .2 HashMap 在 jdk8 中相较于 jdk7 在底层实现方面的不同:
jdk 8 :数组 + 单向链表 + 红黑树
1. 当使用空参的构造器,创建对象时,底层并没创建长度为 16 的数组。
2. 当我们首次调用 put() 添加数据时,底层会首先创建长度为 16 的数组。
3. 底层创建的数组为: Node[]. (class Node implements Map.Entry) 。
4. 当某个索引位置上的链表长度 >8 ,且数组的总长度 >64
时,将此索引位置上的元素修改为使用红黑树进行存储。
5. 形成链表时,新添加的元素在链表的结尾。
5.3 LinkedHashMap 的底层实现原理
LinkedHashMap 的底层实现:在 HashMap 底层结构的基础上,添加了双向链表。
底层实现:
//LinkedHashMap 的内部类 Entry ,定义如下:
static class Entry extends HashMap.Node {
Entry before, after;// 双向链表结构
Entry(int hash, K key, V value, Node next) {
super(hash, key, value, next);
}
}
主要定义一个静态类,创建了一个before 前置节点和一个 after 后置节点。
5.4 TreeMap 的实现
public class TreeMap extends AbstractMap implements
NavigableMap, Cloneable, java.io.Serializable
Java 的 TreeMap 是集合框架中的一个实现类, TreeMap 继承了 AbstractMap 。
TreeMap 实现了 NavigableMap 接口,提供了多种方便的查找功能;
TreeMap 实现了 Cloneable 接口,可以克隆;
TreeMap 实现了 Serialiable 接口,可以序列化。
TreeMap 常用方法:
public V put(K key, V value) 添加一对键值对
public V remove(Object key) 删除对应的键值对
public void clear() 删除所有元素
public Map.Entry ceilingEntry(K key) 返回 map 中键值不小于参数 key
的最小键值对应的键值对,如果没有则返回 null
public final boolean containsKey(Object key) 判断是否含有某一键值
public boolean containsValue(Object value) 判断是否含有某一 value
public V replace(K key, V value) 修改一对键值对
public Comparator comparator() 返回该 TreeMap
的比较器
接下来new 一个 TreeMap 简单实现一下 Map 提供的一些方法:
public class TreeMapTest {
public static void mapain(String[] args) {
Map map = new TreeMap();
// 添加
map.put(" 疯狂 ;
map.put(" 疯狂 Android 讲义 ", 89);
map.put(" 疯狂 Python 讲义 ", 179);
map.put(" 疯狂 Htmapl 讲义 ", 119);
map.put(" 疯狂 Ajax 讲义 ", 109);
// 修改
map.put(" 疯狂 Ajax 讲义 ", 18);
System.out.println(" 是否包含值为 疯狂 Java 讲义 key"+
map.containsKey(" 疯狂 ;
System.out.println(" 是否包含值为 89 key"+
map.containsValue(89));
// 迭代器遍历遍历 value : values()
Collection values = map.values();
Iterator iterator = values.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// 遍历 key-value 方式一: keySet()+map.get(key)
for(Object obj : map.keySet()){
System.out.println(obj + "--->" + map.get(obj));
}
// 遍历 key-value 方式二 entrySet()
Set> entrySet = map.entrySet();
Iterator> iterator2 = entrySet.iterator();
while(iterator2.hasNext()){
// 修改遍历出样式
// mapap.Entry entry = (mapap.Entry)iterator2.next();
//
System.out.println(entry.getKey()+"---->"+entry.getValue());
System.out.println(iterator2.next());
}
map.clear(); // 清空 map
System.out.println(map.isEmpty()); // 是否为空
}
}
5.5 使用 Properties 读取配置文件
// 处理属性文件
public void test1() throws FileNotFoundException,
IOException{
Properties pros = new Properties();
pros.load(new FileInputStream("jdbc.properties"));
String user = pros.getProperty("user");
System.out.println(user);
}
涉及了I/O 那块的内容,通常都是用 Properties 类来读取数据库连接的一些数据,到后面就知道了。
Collections 工具类 ( 与 Math 一样,直接调用就 OK 了 )
1. 作用:
是一个操作 Set 、 List 和 Map 等集合的工具类
2. 常用方法:
reverse(List) :反转 List 中元素的顺序
shuffle(List) :对 List 集合元素进行随机排序
sort(List) :根据元素的自然顺序对指定 List 集合元素升序排序
sort(List , Comparator) :根据指定的 Comparator 产生的顺序对 List
集合元素进行排序
swap(List , int , int) :将指定 list 集合中的 i 处元素和 j 处元素进行交换
Object max(Collection) :根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection , Comparator) :根据 Comparator
指定的顺序,返回给定集合中的最大元素
Object min(Collection) :根据元素的自然顺序,返回给定集合中的最大元素
Object min(Collection , Comparator) :根据 Comparator
指定的顺序,返回给定集合中的最大元素
int frequency(Collection , Object) :返回指定集合中指定元素的出现次数
void copy(List dest,List src) :将 src 中的内容复制到 dest 中
boolean replaceAll(List list , Object oldVal , Object newVal)
:使用新值替换 List 对象的所旧值
Map
Collection 接口
-List 接口
-Set 接口
Map 接口
1. 存放 key-value 数据
key :不能重复 底层实现使用 set
value :可以重复 底层实现用 Collection
2. 实现类
.HashMap: 主要实现类
.LinkedHashMap: 使用链表的方式维护添加 Map 元素的顺序
.TreeMap
.Hashtable :线程安全低,不建议使用
子类Properties :通常用来处理属性文件,键和值都是 String 类型
3. 常用方法
Object put(Object key,Object value) :添加元素
Object remove(Object key) :移除元素 删除一个 Key-value
void putAll(Map t)
void clear()
Object get(Object key) :获取指定 value 值,若没有找到对应的 key 值,则返回 null
boolean containsKey(Object key)
boolean containsValue(Object value)
int size()
boolean isEmpty()
boolean equals(Object obj)
Set keySet()
Collection values()
Set entrySet()
@Test
public void testMap1() {
Map map = new HashMap<>();
map.put("A", 123);
map.put("B", 456);
map.put("C", null);
map.put("B", 123);
map.put(new Person("D", 20), 110);
// 获取元素个数
System.out.println(map.size());
System.out.println(map);
// 移除元素
map.remove("B");
System.out.println(map);
// 更新 value 值(相当于修改 ,Set 中 set 方法)
map.put("C", 119);
System.out.println(map);
// 获取元素
Object object = map.get("A");
System.out.println(object);
// 判断是否包含某个 Key 值
boolean iscontainsKey = map.containsKey("C");
System.out.println(iscontainsKey);
}
结果:
4
{A=123, B=123, C=null, Person [name=D, age=20]=110}
{A=123, C=null, Person [name=D, age=20]=110}
{A=123, C=119, Person [name=D, age=20]=110}
123
true
Map 的遍历
1.Map 中的 key 是使用 Set 存放,不可重复
2.Map 中的 value 使用 Collection 来存放,可以重复
3. 一个 key-value ,是一个 Entry , Entry 使用 Set 来存放,也是不可重复
4. 在向 Map 中添加元素时,会调用 key 所在类的 equals ()方法,判断两个 key 是否相同
如果相同,添加的是后面的元素
5. 遍历 Map 集合的常用方法 Set keySet() Collection values() Set
entrySet()
@Test
public void testMap2() {
// 1. 遍历 key 的集合
Map map = new HashMap<>();
map.put(" 刘备 ", 48);
map.put(" 关羽 ", 30);
map.put(" 张飞 ", 25);
map.put(null, null);
Set set = map.keySet();
for (Object obj : set) {
System.out.println(obj);
}
// 2. 遍历 value 的集合
System.out.println("---------------------");
Collection values = map.values();
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("-------------------");
// 3. 遍历 key-value 对
// 方式一
Set set2 = map.keySet();
for (Object obj : set2) {
System.out.println("key:"+obj+"-value:"+map.get(obj));
}
System.out.println("-------------------");
// 方式二
Set set3 = map.entrySet();
for (Object object : set3) {
Map.Entry entry=(Entry) object;
Object key=entry.getKey();
Object value=entry.getValue();
System.out.println(key+":"+value);
}
}
结果:
关羽
null
张飞
刘备
---------------------
30
null
25
48
-------------------
key: 关羽 -value:30
key:null-value:null
key: 张飞 -value:25
key: 刘备 -value:48
-------------------
关羽:30
null:null
张飞:25
刘备:48