Java集合中的Map接口怎么使用?
Map(双列集合框架) 1、Map接口及实现类概述 Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。 注:将可变对象用作映射键时必须格外小心。当对象是映射中某个键时,如果以影响 equals 比较的方式更改了对象的值,则映射的行为将是不确定的。此项禁止的一种特殊情况是不允许某个映射将自身作为一个键包含。虽然允许某个映射将自身作为值包含,但请格外小心:在这样的映射上 equals 和 hashCode 方法的定义将不再是明确的。 所有通用的映射实现类应该提供两个“标准的”构造方法:一个 void(无参数)构造方法,用于创建空映射;一个是带有单个 Map 类型参数的构造方法,用于创建一个与其参数具有相同键-值映射关系的新映射。实际上,后一个构造方法允许用户复制任意映射,生成所需类的一个等价映射。尽管无法强制执行此建议(因为接口不能包含构造方法),但是 JDK 中所有通用的映射实现都遵从它。 此接口中包含的“破坏”方法可修改其操作的映射,如果此映射不支持该操作,这些方法将抛出 UnsupportedOperationException。如果是这样,那么在调用对映射无效时,这些方法可以(但不要求)抛出外汇返佣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<key,value>的理解 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<K,V> extends HashMap.Node<K,V> { ??????Entry<K,V> before,after;//双向链表结构 ??????Entry(int hash,K key,V value,Node<K,V> next) { ??????????super(hash,key,value,next); ??????} } 主要定义一个静态类,创建了一个before前置节点和一个after后置节点。 5.4 TreeMap的实现 public?class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>,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<K,V> 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<? super K> comparator() 返回该TreeMap的比较器 接下来new一个TreeMap简单实现一下Map提供的一些方法: public class TreeMapTest { public static void mapain(String[] args) { Map<String,Integer> map = new TreeMap<String,Integer>(); //添加 map.put("疯狂Java讲义",109); 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("疯狂Java讲义")); System.out.println("是否包含值为 89 key"+ map.containsValue(89)); //迭代器遍历遍历value:values() Collection<Integer> values = map.values(); Iterator<Integer> 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<Entry<String,Integer>> entrySet = map.entrySet(); Iterator<Entry<String,Integer>> 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=119,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) { /*Object key = obj; Object value = map.get(obj);*/ 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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |