Map集合的概念及用法

168 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的Key,key和value 都可以是任何引用类型数据。Map的Key不允许重复,即同一个Map对像的任何两个key通过equals方法比较总是返回false; 在这里插入图片描述

public class MapTest {
    public static void main(String[] args) {
        Map map = new HashMap();

        map.put("疯狂java讲义", 109);
        map.put("疯狂iOS讲义", 10);
        map.put("疯狂Ajax讲义", 79);

        map.put("轻量级java EE企业应用实战",99);

        System.out.println(map.put("疯狂iOS讲义", 99));
        System.out.println(map);

        System.out.println("是否包含值为 疯狂iOS讲义 key: "+
                map.containsKey("疯狂iOS讲义"));

        System.out.println("是否包含值为99的 value: " +
                map.containsValue(99));

        for (Object key : map.keySet()) {
            System.out.println(key+"-->"+map.get(key));
        }
        map.remove("疯狂Ajax讲义");
        System.out.println(map);


    }c
}

HashMap 和 Hashtable 实现类

HashMap和Hashtable都是Map接口的典型实现类,它们之间的关系完全类似于ArrayList和Vector的关系:

public class Tes01 {
    public static void main(String[] args) {
//        无序,唯一
        Map map = new HashMap();
        map.put("a",1);
        map.put("b",2);
        map.put("c",3);
        map.put("d",4);
        map.put("e",5);
        map.put(null,89);
        System.out.println(map.size());
        System.out.println(map);
        System.out.println(map.containsKey("a"));
        System.out.println(map.containsValue(1));

        Map map2 = new HashMap();
        map2.put("a",1);
        map2.put("b",2);
        map2.put("c",3);
        map2.put("d",4);
        map2.put("e",5);
        System.out.println(map == map2);
        System.out.println(map.equals(map2));

        Set<String> set = map.keySet();
        for (String s : set) {
            System.out.println(s);
        }

        Collection<Integer> values = map.values();
        for (Integer i : values) {
            System.out.println(i);
        }
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> e : entries) {
            System.out.println(e.getKey()+"--"+e.getValue());
        }

    }

}

Hashtable是一个古老的Map实现类,它从JDK1.0就出现了。

java8改进了 HashMap的实现,使用HashMap存在key冲突时依然具有较好的性能。

  1. Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap的性能比Hashtable的性能高一点,但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好。
  2. Hashtable不允许使用null作为key和value,如果试图把null值放进Hashtable中,将会应发NullPointerException异常,但HasHMap可以使用null作为key或value
public class Test02 {
    public static void main(String[] args) {
        Map map3 = new Hashtable();
        map3.put("a",1);
        map3.put("b",2);
        map3.put("c",3);
        map3.put("d",4);
        map3.put("e",5);
//        不能存入空值,运行时会报错
        map3.put(null,null);
        System.out.println(map3.size());
        System.out.println(map3);
        System.out.println(map3.containsKey("a"));
        System.out.println(map3.containsValue(1));
    }
}

LinkedHashMap实现类

HashSet 有一个LinkedHasahSet子类,HashMap 也有一个LinkedHashMap子类;LinkedHashMap也使用双向链表来维护 key-value 对的次序(其实只需要考虑key的次序),该链表负责维护Map的迭代顺序,迭代顺序与key-value 对的插入顺序保持一致。

LinkedHashMap 可以避免对HashMap, Hashtable里的key-value 对进行排序(只要插入key-value对时保持顺序即可),同时又避免使用TreeMap所增加的成本。

public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap score = new LinkedHashMap();
        score.put("语文", 80);
        score.put("英文", 82);
        score.put("数学", 76);

        score.forEach((key, Value) -> System.out.println(key+"--->"+Value));
    }
}

使用Properties 读写属性文件

Properties 类是Hashtable类的子类,正如它的名字所暗示的,该对象在处理属性文件时特别方便(windows操作平台上的ini文件就是一种属性文件)。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的"属性名=属性值"加载到Map对象中。由于属性文件里的属性名,属性值只能是字符串类型,所以Propertise里的keys,value都是字符串类型。

public class PropertiesTest {
    public static void main(String[] args) throws Exception{
        Properties props = new Properties();
        props.setProperty("usernames","yeeku");
        props.setProperty("password","123456");

        props.store(new FileOutputStream("D:\\a.ini"),
                "comment line");
        System.out.println(props);

        Properties props2 = new Properties();
        props.setProperty("gender","male");
        props2.load(new FileInputStream("D:\\a.ini"));

        System.out.println(props2);
        System.out.println(props);
        
    }
}

SortedMap接口 和 TreeMap实现类

正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类一样,Map也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类。

TreeMap 就是一个红黑树数据结构,每个 key-value对即作为红黑树的一个节点。TreeMap存储 key-value 对(节点)时,需要根据key对节点进行排序。TreeMap 可以保证所有的 key-value 对处于有序状态。TreeMap也有两种排序方式。

  1. 自然排序: TreeMap 的所有key 必须实现 Comparable 接口,而且所有的key 应该说同一个类的对象,否则将会抛出ClassCastException 异常。
  2. 定制排序:创建TreeMap时,传入一个Comparator 对象,该对象负责对TreeMap中的所有的key进行排序。采用定制排序时不要求Map的key实现Comparable接口。
public class Student implements Comparable<Student>{
    private int age;
    private String name;
    private double height;

    public Student() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public Student(int age, String name, double height) {
        this.age = age;
        this.name = name;
        this.height = height;

    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", height=" + height +
                '}';
    }

    @Override
    public int compareTo(Student o) {
//        return  this.getAge()-o.getAge();
        return (this.getName()).compareTo(o.getName());
    }
}
public class Test01 {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();

        map.put("a",1);
        map.put("b",2);
        map.put("c",3);
        map.put("d",4);
        map.put("e",5);
        System.out.println(map);
        System.out.println(map.size());
    }
}
public class Test03 {
    public static void main(String[] args) {
        Map<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return ((Double)o1.getHeight()).compareTo((Double)o2.getHeight());
            }
        });
        map.put(new Student(19,"a",170.5),1);
        map.put(new Student(19,"b",170.5),2);
        map.put(new Student(19,"c",170.5),3);
        map.put(new Student(19,"d",170.5),4);
        map.put(new Student(19,"e",170.5),5);
        System.out.println(map);
        System.out.println(map.size());

    }
}

WeakHashMap 实现类

weakHashMap 与 HashMap 的用法基本相似。与HashMap的区别在于,HashMap 的 key保留了对实际对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾收回,HashMap 也不会自动删除这些key 所对应的key-value对;但WeakHashMap 的Key只保留了对实际对象的弱引用,这意味着如果WeakHashMap 对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被回收,WeakHashMap也可能自动删除这些Key 所对应的key-value对。

public class WeakHashMapTest {
    public static void main(String[] args) {
        WeakHashMap whm = new WeakHashMap();
        whm.put(new String("语文"),new String("良好"));
        whm.put(new String("数学"),new String("及格"));
        whm.put(new String("英文"),new String("中等"));

        whm.put("java",new String("中等"));

        System.out.println(whm);

        System.gc();

        System.runFinalization();
        System.out.println(whm);

    }
}

IdentityHashMap 实现类

这个 Map 实现类的实现机制与 HashMap 基本相似,但它处理两个key 相等时比较独特 : 在IdentityHashMap中,当且仅当两个key严格相等(key1 == key2)时,IndentityHashMap才认为两个key相等;

public class IdentityHashMapTest {
    public static void main(String[] args) {
        IdentityHashMap ihm = new IdentityHashMap();
//        下面两行代码将会向IdentityHashMap对象中添加两个key-value;
        ihm.put(new String("语文"),89);
        ihm.put(new String("语文"),78);
//        下面两行代码将会向IdentityHashMap对象中添加一个key-value;
        ihm.put("java", 93);
        ihm.put("java", 98);
        System.out.println(ihm);
        
    }
}

EnumMap

EnumMap是一个与枚举类一起使用的Map实现, EnumMap 中的所有key 都必须是单个枚举类的枚举类。 创建EnumMap时必须显示或隐式指定它对应的枚举类。

enum Season{
    SPRING,SUMMER,FALL,WINTER
}
public class EnumMapTest {
    public static void main(String[] args) {
        EnumMap enumMap = new EnumMap(Season.class);
        enumMap.put(Season.SUMMER, "夏日炎言");
        enumMap.put(Season.SPRING, "春暖花开");
        System.out.println(enumMap);

    }
}

各Map实现类的性能分析

对于 Map的常用实现类而言,虽然HashMap和 Hashtable 的实现机制几乎一摸一样,但由于Hashtable 是一个古老的,线程安全的集合,因此HashMap 通常比Hashtable 要快。

TreeMap 通常比 HashMap, Hashtable要慢,(尤其在插入,删除key-value对时更慢), 因为TreeMap底层采用红黑树来管理 key-value 对 (红黑树的每一个节点就是一个key-value)

使用TreeMap 有一个好处:TreeMap 中的 key-value对总是处于有序状态,无序专门进行排序操作。当TreeMap 被填充之后,就可以调用keySet(),取得由key组成的Set,然后使用toArray()方法生成key的数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速地查询对象。

应该多使用HashMap, 因为HashMap查询快(底层是数组来存储key-value对),如果需要一个总是一个排好序的Map时,则可以考虑使用TreeMap.

LinkedHashMap 比 HashMap 慢一点,因为它需要维护链表来保持Map中key-value时的添加顺序,IdentityHashMap 性能没有特别之处,和HashMap基本相似的实现,只是它使用==而不是equals() 方法来判断元素相等。EnumMap性能最好,但它只能使用同一个枚举类的枚举值作为key。

java提供了一个操作Set, List 和 Map等集合的工具类: Collections,

public class Test01 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("cc");

        list.add("bb");
        list.add("cc");
        Collections.addAll(list,"dd","ee","ff");
        Collections.addAll(list,new String[]{"gg","oo","pp"});

        System.out.println(list);
//        binarySearch二分查找
        Collections.sort(list);//升序方式
        System.out.println(list);
        System.out.println(Collections.binarySearch(list, "cc"));

        System.out.println("====================================");
        ArrayList<String> list2 = new ArrayList<>();
        Collections.addAll(list2,"tt","ss");
//        替换方法
        Collections.copy(list,list2);//将list2上的内容替换到list上面

        System.out.println(list);
        System.out.println(list2);

//        fill 填充
        Collections.fill(list2,"yyy");
        System.out.println(list2);

    }
}

过期接口:Enumeration

public class EnumerationTest {
    public static void main(String[] args) {
        Vector v = new Vector();
        v.add("疯狂java讲义");
        v.add("轻量级java EE企业级应用实战");

        Hashtable score = new Hashtable();
        score.put("语文", 78);
        score.put("数学", 88);
        Enumeration em = v.elements();
        while (em.hasMoreElements()){
            System.out.println(em.nextElement());
        }

        Enumeration keyEm = score.keys();
        while (keyEm.hasMoreElements()){
            Object key = keyEm.nextElement();
            System.out.println(key+"-->"+score.get(key));
        }
    }
}