• Java容器深入浅出之Map、HashMap、Hashtable及其它实现类


    在Java中,Set的底层事实上是基于Map实现的,Map内部封装了一个Entry内部接口,由实现类来封装key-value对,当value值均为null时,key的集合就形成了Set。因此,Map集合具有如下的一些特点:

    1. Key集因为是Set的实现,因此是无顺序、不可重复的。

    2. Value集是List的实现,因此是可以重复的,每个元素根据key来索引。

    3. Map内部包含一个Entry内部接口,用于定义key-value对,由实现类来对外提供查找和设置value的方法。

    Map的基本功能如下:

     1 public class TestMapBasic {
     2 
     3     public static void main(String[] args) {
     4         Map<String, Integer> map = new HashMap<>();
     5         //添加键值对,value可重复
     6         map.put("AAA", 110);
     7         map.put("BBB", 120);
     8         map.put("CCC", 20);
     9         map.put("DDD", 120);
    10         //添加重复key的时候,value会覆盖旧值,方法返回旧值
    11         System.out.println(map.put("CCC", 111));
    12         System.out.println(map);
    13         //通过key和value查找是否存在对应的键值
    14         System.out.println("map.containsKey("BBB")?: " + map.containsKey("BBB"));
    15         System.out.println("map.containsValue(110)?: " + map.containsValue(110));
    16         //遍历Map的增强for循环
    17         for(String key:map.keySet()) {
    18             System.out.println(key + "-->" + map.get(key));
    19         }
    20         //根据key删除value
    21         map.remove("AAA");
    22         System.out.println(map);
    23     }
    24 }

    Map的Java8增强方法示例如下:

     1 public class TestMapAdvance {
     2 
     3     public static void main(String[] args) {
     4         Map<String, Integer> map = new HashMap<>();
     5         //添加键值对,value可重复
     6         map.put("AAA", 110);
     7         map.put("BBB", 120);
     8         map.put("CCC", 20);
     9         map.put("DDD", 120);
    10         //因为map中无ZZZ,因此替换失败
    11         map.replace("ZZZ", 122);
    12         System.out.println(map);
    13         //使用原value与参入参数重新计算value
    14         map.merge("CCC", 20, (oldVal, param) -> (oldVal+param)*2);
    15         System.out.println(map);
    16         //当key为Java,对应的value不存在或null, 计算结果为新value
    17         map.computeIfAbsent("Java", (key) -> key.length());
    18         System.out.println(map);
    19         //当key为Java存在, 则用新结果替换
    20         map.computeIfPresent("Java", (key, value) -> value * 100);
    21         System.out.println(map);
    22     }
    23 }

    HashMap和Hashtable

    HashMap和Hashtable都是Map接口的典型实现类,两者的区别在于:

    1. HashMap是线程不安全的,性能较高,可以使用null作为key或者value

    2. Hashtable是线程安全的,不可以用null做key和value。

    与HashSet一样,HashMap和Hashtable不能保证key-value对的顺序,判断两个元素是否相等的标准为:

    1. 两个key的equals方法比较返回true

    2. 两个key的HashCode方法值相同

    HashMap和Hashtable的containsValue()方法,用于判断是否包含指定的value,判断标准为两个value的equals方法相等即可。因此:

    1. 使用自定义类作为Key时,如果重写equals和HashCode方法,需确保判断标准一致。

    2. 尽量不要使用可变对象作为Key,如果确实需要则不要在程序中修改可变对象。

    LinkedHashMap

    LinkedHashMap在底层使用双链表来维护键值对的顺序,因而性能略低于HashMap,但在迭代集合元素时有较好的性能。

    Properties

    Properties是Hashtable的子类,相当于一个key和value都是String的Map。使用Properties可以方便地把属性文件的“属性名=属性值”转化为Map的键值对,还可以实现Map的键值对与XML文件之间的相互转化,广泛用于JDBC等应用场景中。

     1 public class TestProperties {
     2 
     3     public static void main(String[] args) throws FileNotFoundException, IOException {
     4         Properties props = new Properties();
     5         //为Properties对象添加元素
     6         props.setProperty("username", "admin");
     7         props.setProperty("password", "123456");
     8         //写入文件
     9         props.store(new FileOutputStream("test.properties"), "comment line");
    10         Properties props2 = new Properties();
    11         props2.setProperty("gender", "male");
    12         //将文件中的内容追加到Props2的元素中
    13         props2.load(new FileInputStream("test.properties"));
    14         System.out.println(props2);
    15     }
    16 
    17 }

    TreeMap

    TreeMap是SortedMap接口的实现类,同时也是TreeSet类的底层实现模型。因此具有与TreeSet相似的性质:

    1. TreeMap底层由红黑树的数据结构所实现,每个键值对即为红黑树的一个节点。TreeMap存储键值对时,需要根据Key对节点进行排序。

    2. TreeMap的排序方式包括两种:

    2.1 自然排序:通过Key所实现的Comparable接口来实现,要求Key都是同一个类的对象

    2.2 定制排序:创建TreeMap时,传入一个Comparator对象,负责对Key元素进行排序。

    3. 如果使用自定义类作为Key,重写该类的equals方法和compareTo方法应该一致:equals方法返回true时,compareTo方法要返回0。

    与HashSet相似,HashMap因为存储的键值对是有序的,因此提供了访问第一个、前一个、第一个、最后一个键值对的方法,以及若干截取子TreeMap的方法。

     1 class R implements Comparable<Object>{
     2     
     3     int count;
     4 
     5     public R(int count) {
     6         super();
     7         this.count = count;
     8     }
     9 
    10     @Override
    11     public String toString() {
    12         return "R [count=" + count + "]";
    13     }
    14 
    15     @Override
    16     public boolean equals(Object obj) {
    17         if (this == obj)
    18             return true;
    19         if (obj == null)
    20             return false;
    21         if (getClass() != obj.getClass())
    22             return false;
    23         R other = (R) obj;
    24         if (count != other.count)
    25             return false;
    26         return true;
    27     }
    28 
    29     @Override
    30     public int compareTo(Object o) {
    31         R r = (R)o;
    32         return count > r.count ? 1: count < r.count ? -1 : 0;
    33     }
    34 }
    35 
    36 public class TestTreeMap {
    37 
    38     public static void main(String[] args) {
    39         TreeMap<R, String> tm = new TreeMap<>();
    40         tm.put(new R(3), "AAAA");
    41         tm.put(new R(-5), "BBB");
    42         tm.put(new R(9), "CCCCCC");
    43         System.out.println(tm);
    44         //返回第一个Entry对象
    45         System.out.println(tm.firstEntry());
    46         //返回最后一个Key
    47         System.out.println(tm.lastKey());
    48         //返回比R(2)大一个的Key
    49         System.out.println(tm.higherKey(new R(2)));
    50         //返回比R(2)小的一个Key
    51         System.out.println(tm.lowerKey(new R(2)));
    52         //获取子串
    53         System.out.println(tm.subMap(new R(-1), new R(4)));
    54         System.out.println(tm);    
    55     }
    56 }
    View Code

    WeakHashMap

    WeakHashMap与HashMap的用法基本相似,区别在于:

    1. HashMap的Key保留的是对实际对象的强引用,只要HashMap对象不销毁,所有Key引用对象就不会被垃圾回收;HashMap也不会自动删除这些Key的键值对。

    2. WeakHashMap的Key保留的是对实际对象的弱引用,意味着如果这些Key没有被其它强引用对象所引用,Key对象可能会被垃圾回收;WeakHashMap对象也可能会删除这些键值对。

     1 public class TestWeakHashMap {
     2 
     3     public static void main(String[] args) {
     4         WeakHashMap<String, String> whm = new WeakHashMap<>();
     5         //添加的三个Key对象都是弱引用的匿名类对象
     6         whm.put(new String("Python"), new String("pass"));
     7         whm.put(new String("R"), new String("good"));
     8         whm.put(new String("Scala"), new String("great"));
     9         //添加一个系统缓存的字符串直接量, 为强引用对象,作为Key
    10         whm.put("Java", new String("Number One"));
    11         System.out.println(whm);
    12         //通知系统进行垃圾回收
    13         System.gc();
    14         System.runFinalization();
    15         //此时whm仅保留一个强引用的Java键值对
    16         System.out.println(whm);
    17     }
    18 }

    IdentityHashMap

    IdentityHashMap与HashMap的基本性质和用法大致相同,如可以添加null元素,存储元素的无序性等。核心区别在于判断两个Key对象的标准:

    IdentityHashMap严格要求两个Key必须是Key1 == Key2的严格相等,即堆内存地址相同。

     1 public class TestIdentityHashMap {
     2 
     3     public static void main(String[] args) {
     4         IdentityHashMap<String, Integer> ihm = new IdentityHashMap<>();
     5         //两个new String对象,会被认为是不同的Key,一起添加到集合中
     6         ihm.put(new String("Python"), 100);
     7         ihm.put(new String("Python"), 98);
     8         //字符串直接量Java的内存地址相同,会被认为是同一个对象, 因此只会添加一个(覆盖前值)
     9         ihm.put("Java", 100);
    10         ihm.put("Java", 11);
    11         System.out.println(ihm);
    12     }
    13 }

    总结

    一般的使用中,为了获取较方便的快速查询,使用HashMap较多。特别地,如果需要一个总是排好序的Key-Value集合,则考虑TreeMap。通过TreeMap的KeySet可以直接获取Key集合,然后通过Arrays的工具类转为数组,就可以通过二分法快速查找已经排序完成的对象。

  • 相关阅读:
    什么是同源策略,什么是跨域,如何跨域,Jsonp/CORS跨域
    Scrapy
    爬虫
    Falsk-信号
    python函数中把列表(list)当参数时的"入坑"与"出坑"
    SQLAlchemy基本使用(Flask中)
    列表生成式&生成器表达式
    javascript数据结构——队列
    javascript数据结构——栈
    js数组去重的几种方法
  • 原文地址:https://www.cnblogs.com/leoliu168/p/9910253.html
Copyright © 2020-2023  润新知