java容器深入浅出之maphashmaphashtable及其它实现类

leoliu168      2022-04-18     609

关键词:

在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的工具类转为数组,就可以通过二分法快速查找已经排序完成的对象。

java容器深入浅出之maphashmaphashtable及其它实现类

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

深入理解java合集之框架总览

综述   Java集合就是一个容器。面向对象语言对事物的体现都是以对象的形式存在,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。集合只用于存储对象,集合长度是可变... 查看详情

带各位深入理解java1.8之supplier

supplier也是是用来创建对象的,但是不同于传统的创建对象语法:new,看下面代码:publicclassTestSupplier{privateintage;(www.0831jlyy.com)TestSupplier(){System.out.println(age);}publicstaticvoidmain(String[]args){//创建Supplier容器,声明为TestSup 查看详情

ios之深入解析高阶容器的原理和应用

一、前言我们都知道iOS提供了三种主要的容器类型,它们分别是Array、Set和Dictionary,用来存储一组值:Array:存储一组有序的值;Set:存储一组无序的、不重复的值;Dictionary:存储一组无序的键-值映射。上图都是我们平时用到... 查看详情

springioc源码深入剖析bean的实例化(代码片段)

...流程与继承关系3Bean是怎么实例化的3.1找到ioC源码的入口4容器13大模板方法实例化bean4.1容器13大模板方法之一:prepareRefresh()4.2容器13大模板方法之二:obtainFreshBeanFactory()4.3容器13大模板方法之三:prepareBeanFactory(beanFactor... 查看详情

19.kubernetes深入pod之容器共享volume(代码片段)

Pod容器共享Volume在同一个Pod中多个容器能够共享Pod级别的存储卷Volume,如图:在下面的例子中,Pod内包含两个容器:tomcat和busybox,在Pod级别设置Volume“app-logs”,用于tomcat向其中写日志文件,busybox读取日志文件配置文件pod... 查看详情

带你深入理解stl之deque容器

在介绍STL的deque的容器之前,我们先来总结一下vector和list的优缺点。vector在内存中是分配一段连续的内存空间进行存储,其迭代器采用原生指针即可,因此其支持随机访问和存储,支持下标操作符,节省空间。但是其在分配的内... 查看详情

带你深入理解stl之deque容器

在介绍STL的deque的容器之前,我们先来总结一下vector和list的优缺点。vector在内存中是分配一段连续的内存空间进行存储,其迭代器采用原生指针即可,因此其支持随机访问和存储,支持下标操作符,节省空间... 查看详情

带你深入理解stl之vector容器

...,预估空间不当会引起很多不便。STL实现了一个Vector容器,该容器就是来改善数组的缺点。v 查看详情

带你深入理解stl之stack和queue

上一篇博客,带你深入理解STL之Deque容器中详细介绍了deque容器的源码实现方式。结合前面介绍的两个容器vector和list,在使用的过程中,我们确实要知道在什么情况下需要选择恰当的容器来满足需求和提升效率。一般... 查看详情

深入spring之web.xml

    针对web.xml我打算从以下几点进行解析:        1、ContextLoaderListener: 启动Web容器时,自动装配ApplicationContext的配置信息。      &nb 查看详情

带你深入理解stl之list容器

...行内存的拷贝。为了克服这些缺陷,STL定义了另一种容器List&#x 查看详情

css深入理解学习笔记之padding

1、padding与容器尺寸之间的关系  对于block水平元素:①padding值暴走,一定会影响尺寸;②width非auto,padding影响尺寸;③width为auto或box-sizing为border-box,同时padding值没有暴走,不影响尺寸。  对于inline水平元素:水平padding... 查看详情

深入剖析java并发之阻塞队列linkedblockingqueue与arrayblockingqueue

关联文章:深入理解Java类型信息(Class对象)与反射机制深入理解Java枚举类型(enum)深入理解Java注解类型(@Annotation)深入理解Java类加载器(ClassLoader)深入理解Java并发之synchronized实现原理Java并发编程-无锁CAS与Unsafe类及其并发包Atomic深... 查看详情

深入java源码解析容器类listsetmap

1常用容器继承关系图先上一张网上的继承关系图个人觉得有些地方不是很准确,比如Iterator不是容器,只是一个操作遍历集合的方法接口,所以不应该放在里面。并且Map不应该继承自Collection。所以自己整理了一个常用继承关系... 查看详情

12,k8s之深入理解pod对象

Pod基本概念: 最小部署单元 一组容器的集合 一个Pod中的容器共享网络命名空间 Pod是短暂的Pod实现机制与设计模式: 共享网络 共享存储 [root@centos7demo]#catb.ymlapiVersion:v1kind:Podmetadata:name:my-podspec:containers:-name:writeimage:centoscommand:["ba... 查看详情

java面试之容器

18.Java容器都有哪些?Java容器分为Collection和Map两大类,其下又有很多子类,如下所示:CollectionListArrayListLinkedListVectorStackSetHashSetLinkedHashSetTreeSetMapHashMapLinkedHashMapTreeMapConcurrentHashMapHashtable19.Collection和Co 查看详情

java面试之容器

18.Java容器都有哪些?Java容器分为Collection和Map两大类,其下又有很多子类,如下所示:CollectionListArrayListLinkedListVectorStackSetHashSetLinkedHashSetTreeSetMapHashMapLinkedHashMapTreeMapConcurrentHashMapHashtable19.Collection和Co 查看详情