Map 的介绍
Map 是 Java 中用于存储键值对的集合接口,常见实现包括 HashMap 、TreeMap 和 LinkedHashMap。它允许通过键快速查找对应的值。
Map 接口实现类的特点【很实用】
注意:这里讲的是 JDK8 的 Map接口特点
1)Map 与 Collection 并列存在。用于保存具有映射关系的数据:Key-Value
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class map_ {
public static void main(String[] args) {
// 1)Map 与 Collection 并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
Map map = new HashMap();
map.put("no1", "张无忌"); // k-v
map.put("no2","张飞"); // k-v
System.out.println("map = " + map);
}
}
2)Map 中的 key 和 values 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中,当有相同的 key ,就会替换掉原来的 value
3)Map中的 key 不允许重复,原因和 HashSet 一样
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1", "张无忌"); // k-v
map.put("no2","张飞"); // k-v
map.put("no1","张三丰");
System.out.println("map = " + map);
}
}
4)Map 中的 value 可以重复
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1", "张无忌"); // k-v
map.put("no2","张飞"); // k-v
map.put("no3","张无忌");
System.out.println("map = " + map);
}
}
5)Map 的 key 可以为 null,value 也可以为null,注意 key 为null,只能有一个,value 为 null,可以多个
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put(null,null);
map.put(null,"你好");
System.out.println("map = " + map);
}
}
6)常用 String 类作为 Map 的 Key (常用的是 String 类,但也可以是其他类型, Object 的子类)
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put(1,"阳光");
map.put(new Object(),"周芷若");
System.out.println("map = " + map);
}
}
7)key 和 value 之间存在单向一对关系,即通过 get 方法,指定的 key 总能找到对应的 value
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put(1,"阳光");
map.put(new Object(),"周芷若");
map.put("no1","金毛狮王");
map.put(22.0,"小龙女");
map.put("no2","abcd");
System.out.println("map = " + map);
// 通过 get 方法,传入 key,返回相应的 value
System.out.println(map.get("no2"));
}
}
8)Map 存放数据的 key-value 示意图,一对 k - y是放在一个 HashMap$Node 中的,有因为 Node 实现了 Entry 接口,有些书上也说 一对 k - y 就是一个 Entry(如图)
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("first", "贾宝玉");
map.put("Second", "林黛玉");
// 解读
// 1. k-v 最后是 HashMap$Node node = newNode(hash , key , value , null)
// 2. k-v 为了程序员遍历,还会创建 EntrySet 集合,该集合存放的元素的类型 Entry ,而一个 Entry
// 对象就有 k-v EntrySet<Entry<k,v>>,即:transient Set<Map.Entry<k,v>> entrySet;
// 3. entrySet 中,定义的类型是 Map.Entry,但是实际上存放的还是HashMap$Node
// 这是因为 static class Node<k,v> implements Map.Entry<k.v>
// 4. 当把 HashMap$Node 对象 存放到 entrySet 就方便我们的遍历
// k getKey() v getValue()
Set set = map.entrySet();
System.out.println(set.getClass()); // HashMap$Node
for (Object obj : set) {
// System.out.println(obj.getClass());
// 为了从HashMap$Node 中取出 k-v
// 1. 先做一个向下转型
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
}
}
Map接口和常用方法
1)put 添加
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put(null,"书籍");
System.out.println("map = " + map);
}
}
2)remove 根据键删除映射关系
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put("交通工具","地铁");
map.put(null,"书籍");
System.out.println("map = " + map);
map.remove(null);
System.out.println("使用 remove 方法后的 map = " + map);
}
}
3)get 根据键获取值
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put("交通工具","地铁");
Object value = map.get("交通工具");
System.out.println(value); //通过 key 得到 value
}
}
4)size获取元素个数
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put("交通工具","地铁");
System.out.println(map.size());
}
}
5)isEmpty 判断个数是否为0
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put("交通工具","地铁");
System.out.println(map.isEmpty());
}
}
6)clear 清除
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put("交通工具","地铁");
map.clear();
System.out.println("map = " + map);
}
}
7)containsKey 查找键是否存在
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put("交通工具","地铁");
System.out.println(map.containsKey("蔬菜")); // 返回 true
}
}
Map 遍历方式案例演示
1)containsKey 查找键是否存在
2)keySet 获取所有的键
java
package com.heima.Hello.Map_;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("水果","苹果");
map.put("蔬菜","西兰花");
map.put("蔬菜","萝卜");
map.put("交通工具","地铁");
map.put("糕点","吐司");
System.out.println("map = " + map);
// 先取出所有的 key,再通过 key 取出对应的 value
Set key_set = map.keySet();
}
}
3)entrySet 获取所有的键
4)values 获取所有的值
HashMap 底层机制及源码剖析
扩容机制 [ 和 HashSet 相同 ]
1)HashMap 底层维护了 Node 类型的数据 table,默认为 null
2)当创建对象时,将加载因子(loadfactor)初始化为 0.75
3)当添加 key-val 时,通过 key 的哈希值得到在 table 的索引。然后判断该索引是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的 key 和准备加入的 key 是否相等,如果相等,则直接替换 val ;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。
4)第一次添加,则需要扩容 table 容量为 16,临界值(threshold)为 12(16*0.75)
5)以后再扩容,则需要扩容 table 容量为原来的 2 倍(32),临界值为原来的 2 倍,即 24,依次类推。
6)在 Java 8 中,如果一条链表的元素个数超过 TREEIFY_THRESHOLD(默认是 8),并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
总结---开发中如何中如何选择集合实现类(记住)
在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择,分析如下:
1)先判断存储的类型(一组对象[单列]或一组键值对 [双列] )
2)一组对象:Collection 接口
允许重复 :List
增删多:LinkedList [ 底层维护了一个双向链表 ]
改查多:ArrayList [ 底层维护 Object 类型的可变数组 ]
不允许重复:Set
无序:HashSet [ 底层是 HashMap,维护了一个哈希表,即(数组 + 链表 + 红黑树)]
排序:TreeSet
java
package com.heima.Hello.Map_;
import java.util.Comparator;
import java.util.TreeSet;
@SuppressWarnings({"all"})
public class TreeSet_ {
public static void main(String[] args) {
// 解读
//1. 当我们使用无参构造器,创建 TreeSet 时,仍然是无序的
//2. 希望添加的元素,按照字符串大小来排序
//3. 使用 TreeSet 提供一个构造器,可以传入一个比较器(原名内部类)
// 并指定排序规则
//4. 简单看看源码
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// 下面这句话是调用 String 类的 compare方法进行字符串比较
return ((String) o1).compareTo((String) o2);
}
});
treeSet.add("a");
treeSet.add("tree");
treeSet.add("hello");
treeSet.add("world");
System.out.println("treeset: " + treeSet);
}
}
插入和取出顺序一致:LinkedHashSet。维护数组 + 双向链表
3)一组键值对:Map
键无序:HashMap [底层是:哈希表 jdk 7:数组 + 链表,jdk 8:数组 + 链表 + 红黑树]
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件 Properties