Map接口

  • 1,Map接口的引入
  • 2,介绍Map接口的五个主要实现类
  • 3,HashMap的底层实现原理
  • 3.1,HashMap在jdk7中的底层实现原理
  • 3.2,HashMap在jdk8中的底层实现原理
  • 4,Map接口中的常用方法
  • 5,TreeMap两种添加方式
  • 6,Properties处理属性文件
  • 7,Collections工具类的使用
  • 7.1,Collections常用方法


1,Map接口的引入

java map接口实现类 map接口实现类有哪些_开发语言

2,介绍Map接口的五个主要实现类

Map五个主要实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、Properties。

其中LinkedHashMap是HashMap的一个子类。Properties是HashTable的一个子类。


①HashMap是Map接口的主要实现类,效率高,线程不安全。HashMap可以存储null的key和value。
②LinkedHashMap:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。保证在遍历map元素时,可以按照添加的顺序实现遍历。对于频繁的遍历操作此类的执行效率高于HashMap。
③TreeMap:保证按照添加的key-value进行排序,实现排序遍历,按key排序,所以此时考虑key的自然排序或定制排序,底层使用红黑树(和TreeSet类似)。
④HashTable线程安全,效率低;不能存储null的key和value。不常用。
⑤Properties常用来处理配置文件。是Hashtable子类,其key和value均为String类型


把握面试题:HashMap和Hashtable的异同。


3,HashMap的底层实现原理

java map接口实现类 map接口实现类有哪些_后端_02

①Map中的key是无序的、不可重复的。使用Set存储所有的key(对于HashMap使用HashSet存key、LinkedHashMap使用LinkedHashSet存key----key所在类要重写equals()和hashCode());

②Map中的value:无序的,可重复的,使用Collection存储所有的value----value所在类只要求重写equals();

③一个键值对:key-value构成了一个Entry对象。Map中的Entry无序,不可重复,使用Set存储所有Entry。

3.1,HashMap在jdk7中的底层实现原理

①HashMap map=new HashMap(); ----空参构造器创建map对象。实例化之后,底层会创建一个长度为16的一维数组Entry[ ] table。(数组类型为Entry)。
②添加一些数据后,再次执行map.put(key1,value1); 首先调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算后,得到在Entry数组中的存放位置。如果此位置上数据为空,则key1-value1添加成功。-------情况一
如果此位置上数据不为空,意味着此位置存在一个或多个数据(以链表形式存在)。比较当前key1和已经存在的一个或多个数据的哈希值。如果key1哈希值和已存在数据哈希值都不相同,此时key1-value1能添加成功;------情况二
如果key1哈希值和已存在的某一个数据(key2-value2)的哈希值相同。则继续调用key1所在类的equals(key2)方法比较。如果equals()返回false,则key1-value1添加成功。------情况三
如果equals()返回true,使用value1替换value2。相当于此时的put方法具有修改的功能。如:原来放的是(“Tom”,23),put一下(“Tom”,45),显然二者哈希值和equals()均为true。执行完存的是(“Tom”,45)。

扩容问题:默认扩容方式为扩容为原来的2倍,并将原有数据复制过来。

补充:jdk7中,关于情况二和情况三,此时的key1-value1和原来的数据以链表的形式存储。

3.2,HashMap在jdk8中的底层实现原理

jdk8相较于jdk7在底层实现的不同:
①new HashMap()时,底层没有创建长度为16的数组
②jdk8中底层数组是Node[ ],而非Entry[ ]
③首次调用put方法时,底层创建长度16的数组
④jdk7底层结构只有数组加链表,jdk8中底层结构是:数组加链表加红黑树。当数组的某一个索引位置上的元素以链表存在的数据个数超过8,且当前数组长度超过64,则此时索引位置上的所有数据改为使用红黑树(二叉排序树的一种)存储。(这样就会提高遍历、查找的效率)

HashMap的底层实现原理也是一道高频面试题,必须掌握。

4,Map接口中的常用方法

java map接口实现类 map接口实现类有哪些_开发语言_03

演示:

package com.atguigu.java;
import org.junit.Test;
import java.util.*;
public class MapTest {
    /* 添加、删除、修改操作:
     *  Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
     *  void putAll(Map m):将m中的所有key-value对存放到当前map中
     *  Object remove(Object key):移除指定key的key-value对,并返回value
     *  void clear():清空当前map中的所有数据
     */
    @Test
    public void test1(){
        Map map=new HashMap();
        //添加
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);
        //修改
        map.put("AA",87);
        System.out.println(map); //{AA=87, BB=56, 45=123}

        Map map1=new HashMap();
        map.put("CC",123);
        map.put("DD",123);
        map.putAll(map1);
        System.out.println(map); //{AA=87, BB=56, CC=123, DD=123, 45=123}

        //Object remove(Object key):移除指定key的key-value对,并返回 value。没有时返回null
        Object value=map.remove("CC");
        System.out.println(value); //123
        System.out.println(map); //{AA=87, BB=56, DD=123, 45=123}

        map.clear();  //只清空数据,map还在
        System.out.println(map); //{}
        System.out.println(map.size()); //0
    }
    @Test
    public void test2(){
        /*
          元素查询的操作:
         *  Object get(Object key):获取指定key对应的value
         *  boolean containsKey(Object key):是否包含指定的key
         *  boolean containsValue(Object value):是否包含指定的value
         *  int size():返回map中key-value对的个数
         *  boolean isEmpty():判断当前map是否为空
         *  boolean equals(Object obj):判断当前map和参数对象obj是否相等
         */
        Map map=new HashMap();
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);
        System.out.println(map.get(45));  //123

        boolean isExist = map.containsKey("BB"); //containsKey要先找哈希值,判断位置。再调用equals()
        System.out.println(isExist); //true

        boolean isExist2 = map.containsValue(123);
        System.out.println(isExist2);  //true

        System.out.println(map.size()); //3

        System.out.println(map.isEmpty());  //false

        Map map1=new HashMap();
        map1.put("AA",123);
        map1.put(45,123);
        map1.put("BB",56);
        System.out.println(map1.equals(map));  //true
    }
    @Test
    public void test3(){
        /* 说明:针对Collection遍历可以使用迭代器;Map中没有迭代器。
        但是,Map是由key-value构成,
                ①所有的key都是无序,不可重复,使用Set存储的。所以想遍历所有的key,只需拿到此Set,然后去 ".iterator()";
                ②value无序、可重复的,单列数据,使用Collection存储。所以拿到 此Collection,然后 ".iterator()";
                ③键值对key-value构成Entry对象。Map中Entry无序、不可重复,使用Set存储。拿到所以Entry构成的Set,再去 ".iterator()"
        所以关键是怎么拿到所有key构成的Set、value构成的Collection、Entry构成的Set? 正好对应以下三个方法:
        *  元视图操作的方法:
         *  Set keySet():返回所有key构成的Set集合
         *  Collection values():返回所有value构成的Collection集合
         *  Set entrySet():返回所有key-value对构成的Set集合
        * */
        Map map=new HashMap();
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);

        //①遍历所有的key集:keySet()
        Set set = map.keySet();
        //System.out.println(set);  //[AA, BB, 45]
        Iterator iterator = set.iterator();  //iterator()方式遍历
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("***********");
        //②遍历所有的value集:values()
        Collection values = map.values();
        for (Object obj:values){   //foreach循环方式遍历
            System.out.print(obj+" "); //123 56 123
        }
        System.out.println(); //换行
        System.out.println("***********");

        //③遍历所有的key-value集:entrySet()返回Set型
        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();  //此处的obj实际是一个个的Entry
            Map.Entry entry=(Map.Entry)obj;  //强转。Map接口中定义了一个内部的静态接口Entry
            //Entry中有抽象方法getKey()、getValue()。
            System.out.println(entry.getKey()+"--->"+entry.getValue());
        }
        System.out.println("***********");

        //成对输出的另一种方式:
        Set keySet = map.keySet();
        Iterator iterator2 = set.iterator();
        while (iterator2.hasNext()){
            Object key = iterator2.next(); //此处 iterator2.next()拿到的是key,而通过key可以找value
            Object value=map.get(key); // Object get(Object key):获取指定key对应的value
            System.out.println(key+"<--->"+value);
        }
    }
}

其中test3()的运行结果为:

java map接口实现类 map接口实现类有哪些_java map接口实现类_04

5,TreeMap两种添加方式

需要先了解Java比较器的知识:


Java比较器相关链接:


TreeMap两种添加方式演示:

package com.atguigu.java;
import org.junit.Test;
import java.util.*;
/**
 * 回顾TreeMap实现类:保证按照添加的key-value进行排序,实现排序遍历。
 *                 按key排序,所以此时考虑key的自然排序或定制排序,底层使用红黑树(和TreeSet类似)
 *
 * 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
 * 因为TreeMap要按照key进行排序:自然排序和定制排序。注意按照key排序,不可以按value!!!
 */
public class TreeMapTest {
    //方式一:自然排序
    @Test
    public void test1(){
        TreeMap map=new TreeMap(); //自然排序调空参构造器。User中compareTo()指定按照姓名从大到小排列
        User u1=new User("Tom",23);
        User u2=new User("Jerry",32);
        User u3=new User("Jack",20);
        User u4=new User("Rose",18);
        map.put(u1,98);  //此处value表示成绩
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        //遍历所有的key-value集:entrySet()返回Set型
        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();  //此处的obj实际是一个个的Entry
            Map.Entry entry=(Map.Entry)obj;  //强转。Map接口中定义了一个内部的静态接口Entry
            //Entry中有抽象方法getKey()、getValue()。
            System.out.println(entry.getKey()+"--->"+entry.getValue());
        }
        System.out.println("***********");
    }

    //方式二:定制排序
    @Test
    public void test2(){
        TreeMap map=new TreeMap(new Comparator() {  //定制排序调带参构造器。
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User&&o2 instanceof User){
                    User u1=(User)o1;
                    User u2=(User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge()); //按年龄比,从小到大
                }
                throw new RuntimeException("输入的类型不匹配");
            }
        });
        User u1=new User("Tom",23);
        User u2=new User("Jerry",32);
        User u3=new User("Jack",20);
        User u4=new User("Rose",18);
        map.put(u1,98);  //此处value表示成绩
        map.put(u2,89);
        map.put(u3,76);
        map.put(u4,100);

        //遍历所有的key-value集:entrySet()返回Set型
        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();  //此处的obj实际是一个个的Entry
            Map.Entry entry=(Map.Entry)obj;  //强转。Map接口中定义了一个内部的静态接口Entry
            //Entry中有抽象方法getKey()、getValue()。
            System.out.println(entry.getKey()+"--->"+entry.getValue());
        }
    }

}

运行结果:

java map接口实现类 map接口实现类有哪些_java map接口实现类_05

6,Properties处理属性文件

java map接口实现类 map接口实现类有哪些_java map接口实现类_06

7,Collections工具类的使用

操作数组的工具类为Arrays,操作集合的工具类为Collections。

java map接口实现类 map接口实现类有哪些_开发语言_07

7.1,Collections常用方法

java map接口实现类 map接口实现类有哪些_后端_08

java map接口实现类 map接口实现类有哪些_intellij-idea_09

package com.atguigu.java;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
 * Collections:操作Collection、Map的工具类
 *
 * 注意:
 * sort(List):根据元素的自然顺序(调用Integer里的compareTo()方法)对指定 List 集合元素按升序排序。返回值类型为void
 * sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
 *
 *
 * Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
 * Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
 *
 * Object min(Collection)
 * Object min(Collection,Comparator)
 *
 *
 *
 */
public class CollectionsTest {
    @Test
    public void test1(){
        List list=new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);
        System.out.println(list);  //[123, 43, 765, -97, 0]

        //①reverse(List):反转 List 中元素的顺序
        Collections.reverse(list);  //返回值为void,list本身被修改。
        System.out.println(list);  //[0, -97, 765, 43, 123]
        //②shuffle(List):对 List 集合元素进行随机排序,返回值为void
        Collections.shuffle(list);
        System.out.println(list);  //[0, 123, 43, -97, 765]。随机排序,每次运行都可能不一样

        //③sort(List):根据元素的自然顺序(调用Integer里的compareTo()方法)对指定 List 集合元素按升序排序。返回值类型为void
        Collections.sort(list);
        System.out.println(list);  //[-97, 0, 43, 123, 765]
        Comparable max = Collections.max(list);//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
        System.out.println(max); //765
        Comparable min = Collections.min(list);
        System.out.println(min); //-97
        
        //④swap(List,int i, int j):将指定 list 集合中的 i 处元素和 j 处元素进行交换
        Collections.swap(list,1,2); //交换索引1,2位置上元素
        System.out.println(list); //[-97, 43, 0, 123, 765]
    }
    @Test
    public void test2(){
        List list=new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);

        //⑤int frequency(Collection,Object):返回指定集合中指定元素的出现次数
        int frequency = Collections.frequency(list, 765);
        System.out.println(frequency); //3

        //⑥void copy(List dest,List src):将src中的内容复制到dest中。dest的size不能小于src的size,否则会报异常
        //所以想用copy()方法,需要把dest的size变得足够大
        //错误方式:
        //List dest=new ArrayList(list.size());  //此处加参数表示造底层数组的长度,而非有几个元素。所以行不通
        //Collections.copy(dest,list);
        //正确方式:(造一个数组,数组长度就是list的size。巧妙解决问题)
        List dest= Arrays.asList(new Object[list.size()]);  //此处Arrays.asList(),括号内填几个数,size就是几
        System.out.println(dest.size()); //5。dest的size就是list的size
        System.out.println(dest); //[null, null, null, null, null]
        Collections.copy(dest,list);
        System.out.println(dest); //[123, 43, 765, -97, 0]

        //⑦* boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值
        Collections.replaceAll(list,43,34);
        System.out.println(list); //[123, 34, 765, -97, 0]
    }
}

Collections常用方法:同步控制

java map接口实现类 map接口实现类有哪些_java_10


如:ArrayList和HashMap线程都不安全,如果需要考虑线程安全问题,一般也不会选择Vector和Hashtable。而是使用同步控制方法将其转为线程安全的:

java map接口实现类 map接口实现类有哪些_后端_11