首页 > 其他分享 >第十三章《集合》第5节:Map集合

第十三章《集合》第5节:Map集合

时间:2023-01-02 17:02:32浏览次数:59  
标签:Map 13 第十三章 map value key 集合

​List、Set和Queue都是Collection接口的子接口,因此从更高层次来说它们属于统一类型的集合。Map接口也代表一种集合,但它不是Collection子接口,因此它属于另一种类型的集合。Map用于保存具有映射关系的数据。映射关系的数据分为两部分,就好比电话本一样,如图13-20所示。

第十三章《集合》第5节:Map集合_Map集合


图13-20电话本​

从图13-20可以看出:电话本左边记录着人的姓名,右边对应的记录着电话号码。人们在电话本中查询时,总是通过人的姓名找到他的电话号码。按照专业上的说法,电话本中人的姓名被称为键,英文翻译为key,电话号码被被称为值,英文翻译为value,人们总是通过key找到value。key和 value都可以是任何引用类型的数据。Map中的key不允许重复,也就是说同一个Map对象中的任何两个key通过equals()方法比较总是返回false。key和value之间存在单向一对一关系,即通过指定的key总能找到唯一的、确定的value。程序员在访问Map集合时,总是通过指定key来获取相应的value。需要注意:原则上在Map集合中key-value的存储顺序与其进入集合的顺序并不相同,这个特点跟Set集合是一样的。正因为Map集合有key和value两组值,因此在给Map集合加上泛型时,需要同时指定key和value的类型参数,在官方的定义中,key的类型参数名为K,而value的类型参数名为V。​

Map接口定义了很多方法,这些方法大多数都是用来操作集合元素的,如表13-8所示。​

表13-8 Map接口的方法​

方法​

功能​

void clear()​

清空集合​

boolean containsKey(Object key)​

查询Map中是否包含指定的key​

boolean containsValue(Object value)​

查询Map中是否包含指定的value​

Set entrySet()​

返回Map中包含的key-value对所组成的Set集合,每个集合元素都是 Map.Entry(Entry是 Map的内部类)对象​

V get(Object key)​

返回指定key所对应的value,不包含该key时则返回null​

boolean isEmpty()​

判断集合是否为空集合​

Set keySet()​

返回该Map中所有key组成的Set集合​

V put(K key, V value)​

添加一个key-value对,如果当前集合中已有一个与该key相等的key-value对,则新的key-value对会覆盖原来的key-value对​

void putAll(Map m)​

将指定 Map中的key-value对复制到本Map中​

V remove(Object key)​

删除指定key所对应的key-value对,返回被删除key所关联的value,如果该key不存在,则返回null​

boolean remove(Object key, Object value)​

删除指定key、value所对应的key-value对​

int size()​

获取集合中key-value对的个数​

Collection<V> values()​

返回该Map里所有value组成的Collection​

Map接口中定义了一个Entry内部接口,Entry表示key-value对,Entry接口中定义了用来操作一个键值对的方法,如表13-9所示。​

表13-9 Entry接口的方法​

方法​

功能​

K getKey()​

返回该Entry中包含的key​

V getValue()​

返回该Entry中包含的value​

V setValue(V value)​

重新设置该 Entry里包含的value值,并返回新设置的value值​

Map接口有很多实现类,它们的特点和性能各不相同,本小节将详细讲解这些Map实现类的特点和使用方式。​

13.5.1类和Hashtable类

HashMap和Hashtable都是Map接口的典型实现类,它们之间的关系类似于ArrayList和 Vector的关系,Hashtable是一个古老的Map实现类,它从JDK 1.0起就已经出现了,它出现时Java还没有提供Map接口,因此它包含了一些Map所没有的方法。Hashtable和HashMap有以下两个区别:​

  • Hashtable是线程安全的,而HashMap是线程不安全的,所以HashMap比Hashtable的性能高一点,但如果有多个线程访问同一个 Map对象时,使用Hashtable会更好。​
  • Hashtable不允许使用null 作为 key和 value,如果试图把 null 值放进Hashtable 中,将会引发NullPointerException异常,但 HashMap可以使用null作为key或 value ​

由于HashMap中的key不能重复,所以HashMap里最多只有一个key为null,但可以有任意多个value为null。下面【例13_15】以HashMap为例展示了Map集合的特点。​

【例13_15 Map集合的特点】

Exam13_15.java​

import java.util.*;
public class Exam13_15 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();//成对放入多个key-value对
map.put("张三", "13800009999");
map.put("李四", "13655556666");
map.put("王五", "15522223333");
System.out.println("map中的键值对:" + map);
map.put("李四", "15899996666");//放入重复的key
System.out.println("更新后的map:" + map);
System.out.println("map中是否包含值为\"张三\"的key: " + map.containsKey("张三"));
System.out.print("是否包含值为\"15899996666\"的value: ");
System.out.println(map.containsValue("15899996666"));
//通过遍历key获取所有键值对
System.out.println("遍历map:");
for(String key:map.keySet()){
System.out.println(key+":"+map.get(key));
}
map.remove("张三");//删除key为张三的键值对
System.out.println("删除张三后的map:"+map);
}
}

在本例的程序中可以看到:创建Map集合对象时,如果指定类型参数,必须对key和value都做出指定。【例13_15】的运行结果如图13-21所示。​

第十三章《集合》第5节:Map集合_Java_02

图13-21【例13_15】运行结果​

从图13-21可以很明显的看出:向Map集合中添加key-value对时,如果集合中已经有了相同的key,那么新的value会覆盖旧的value。此外还可以看出:Map集合中key-value的存储顺序与其进入集合的顺序并不相同。​

需要注意:HashMap和Hashtable在执行containsValue()方法时需要判断参数提供的value是否与集合中的某个value相同,判断过程中并不会参考value的哈希码值,只调用equals()方法进行判断。如果使用可变类的对象作为HashMap和Hashtable的key,并且程序修改了作为key的可变类对象,则也可能出现无法准确访问到Map中被修改过的key的情况,这与修改HashSet中元素后访问不到该元素的原理相同。​

13.5.2 LinkedHashMap类

HashSet有一个LinkedHashSet子类,HashMap也有一个LinkedHashMap子类。LinkedHashMap也使用双向链表来维护key-value对的次序,因此集合中key-value对的存储顺序与它们加入集合的顺序是一样的。由于LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap,但因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时有较好的性能。下面的【例13_16】展示了迭代输出LinkedHashMap的效果。​

【例13_16 LinkedHashMap类的使用】

Exam13_16.java​

import java.util.*;
public class Exam13_16 {
public static void main(String[] args) {
LinkedHashMap<String,String> map = new LinkedHashMap<String,String>();
map.put("张三", "13800009999");
map.put("李四", "13655556666");
map.put("王五", "15522223333");
System.out.println("map中的键值对:" );
map.forEach((key,value)-> System.out.println(key+"="+value));
}
}

【例13_16】的程序中使用forEach()方法遍历集合,forEach()方法的参数类型是BiConsumer,它是一个函数式接口,因此可以用Lambda表达式来表示它的匿名实现类对象。BiConsumer中定义了一个accept()抽象方法,这个方法有两个参数,程序员只要实现处理这两个参数的过程即可。本例中以map中的key和value作为accept()方法的参数,对它们的操作是同时输出key和value,并在它们之间以=作为间隔【例13_16】的运行结果如图13-22所示。​

第十三章《集合》第5节:Map集合_Java_03

图13-22【例13_16】运行结果​

从图13-22可以很明显看出:key-value对在集合中的顺序与它们加入集合的顺序是完全一致的。​

13.5.3 Properties类

Properties是Hashtable的子类,它常被用于存储配置文件的内容。配置文件有很多中,某些配置文件中的信息就是以key-value对的形式存储的。Properties是Hashtable的子类,所以其数据也是按照key-value对的形式存储的,因此Properties对象用于存储配置文件的内容非常合适。属性文件中的信息都属于字符串,为了与之相适应Properties类的key和value也都是字符串类型。Properties定义了一套属于自身的操作元素的方法,除此之外,由于Properties是专门用来存储配置文件的内容,所以它还定义了一些读写文件的方法,Properties类所定义的方法如表13-10所示。​

表13-10 Properties类的方法​

方法​

功能​

String getProperty(String key)​

根据key获取相应的value​

String getProperty(String key, String defaultValue)​

根据key获取相应的value,如果key不存在返回defaultValue所指定的字符串​

Object setProperty(String key, String value)​

把key-value对存入Properties​

void load(InputStream inStream)​

用字节流把属性文件中的内容读入Properties​

void load(Reader reader)​

用字符流把属性文件中的内容读入Properties​

void store(OutputStream out, String comments)​

用字节流把Properties中的键值对输出到文件​

void store(Writer writer, String comments)​

用字符流把Properties中的键值对输出到文件​

下面的【例13_17】展示了如何使用Properties读写配置文件。​

【例13_17 Properties类的使用】

Exam13_17.java​

import java.io.*;
import java.util.Properties;
public class Exam13_17 {
public static void main(String[] args) {
FileWriter fw = null;
FileReader fr = null;
try {
Properties props = new Properties();
fw = new FileWriter("D:/Exam13_17.ini");
fr = new FileReader("D:/Exam13_17.ini");
props.setProperty("size","255");
props.setProperty("time","3h");
props.setProperty("color","red");
props.store(fw,"comments");//把Properties的内容写到配置文件中
props.setProperty("rate","40");
props.load(fr);//把配置文件中的内容加入Properties
System.out.println("props中的内容:"+props);
}catch (IOException e){
e.printStackTrace();
}
}
}

当运行完程序后,可以在D盘根目录下看到生成了一个Exam13_17.ini文件,文件内容如图13-23所示。​

第十三章《集合》第5节:Map集合_键值对_04

图13-23【例13_17】所生成的文件​

【例13_17】的运行结果如图13-24所示。​

第十三章《集合》第5节:Map集合_Java_05

图13-24【例13_17】运行结果​

从图13-24可以看出:Properties存储的key-value对的顺序与它们加入Properties的顺序并不一定一致,这也是很多Map集合都有的特点。​

13.5.4 TreeMap类

Map接口派生了SortMap子接口,这个子接口有一个实现类TreeMap,TreeMap存储key-value时根据key的值对元素进行排序,因此TreeSet中的元素是以key的值进行排列的。与TreeSet集合一样,TreeMap也有自然排序和定制排序两种方式。如果是自然排序,则要求key实现Comparable接口,并且所有存入TreeMap集合的key都必须是同一种类型,而如果是定制排序,需要借助Comparator接口的实现类对key进行排序,这一点与TreeSet集合的规则是一样的。TreeMap中判断两个key相等的标准是:两个key通过compareTo()方法返回0,TreeMap就认为这两个key是相等的。实际开发过程中,如果重写一个类的equals()方法和compareTo()方法时,应该让两个方法的返回结果保持意义上的一致性,也就是说:两个key通过equals()方法比较返回 true时,它们通过compareTo()方法比较应该返回0。如果equals()方法与compareTo()方法的返回结果意义不一致,TreeMap与Map接口的规则就会产生冲突。​

TreeMap提供了一系列以key顺序访问key-value对的方法。如表13-11所示。​

表13-11 TreeMap类的方法​

方法​

功能​

Map.Entry<K,V> firstEntry()​

返回该Map中最小key所对应的 key-value对,如果该Map为空,则返回null​

K firstKey()​

返回该Map中的最小key值,如果该Map为空,则返回null​

Map.Entry<K,V> lastEntry()​

返回该Map中最大key所对应的 key-value对,如果该Map为空或不存在这样的key-value对,则返回null​

K lastKey()​

返回该Map中的最大key值,如果该Map为空或不存在这样的 key,则都返回null​

Map.Entry<K,V> higherEntry(K key)​

返回该Map中位于key后一位的key-value对(即大于指定key的最小key所对应的key-value对)。如果该Map为空,则返回null​

K higherKey(K key)​

返回该Map中位于key后一位的 key值(即大于指定key的最小key 值)。如果该Map为空或不存在这样的 key-value对,则都返回null​

Map.Entry<K,V> lowerEntry(K key)​

返回该Map中位于key前一位的key-value对(即小于指定key的最大key所对应的key-value对)。如果该Map为空或不存在这样的 key-value对,则都返回null​

K lowerKey(K key)​

返回该Map中位于key前一位的key值(即小于指定key的最大key值)。如果该Map为空或不存在这样的key,则都返回null​

NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)​

返回该Map的子Map,其 key的范围是从fromKey(是否包含取决于fromInclusive)到toKey(是否包含取决于toInclusive)​

SortedMap<K,V> subMap(K fromKey, K toKey)​

返回该Map的子Map,其 key的范围是从fromKey (包括)到toKey (不包括)​

SortedMap<K,V> tailMap(K fromKey)​

返回该Map 的子Map,其 key的范围是大于fromKey(包括)的所有key​

NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)​

返回该Map的子Map,其key 的范围是大于fromKey(是否包括取决于inclusive)的所有key​

SortedMap<K,V> headMap(K toKey)​

返回该Map的子Map,其 key的范围是小于toKey(不包括)的所有key​

NavigableMap<K,V> headMap(K toKey, boolean inclusive)​

返回该Map的子Map,其key 的范围是小于toKey(是否包括取决于第二个参数)的所有key​

表13-11所列的这些方法看起来很复杂,其实它们都很简单,无非就是根据key的排序获得Map集合中的一个或一部分key或者是key-value对。下面的【例13_18】展示了TreeMap集合的特点。​

【例13_18 TreeMap类的使用】

Exam13_18.java​

import java.util.TreeMap;
public class Exam13_18 {
public static void main(String[] args) {
TreeMap tm = new TreeMap();
tm.put(3,"张晓");
tm.put(5,"李明");
tm.put(2,"王红");
tm.put(1,"赵翔");
tm.put(4,"杨洋");
System.out.println("tm中所有键值对:"+tm);
System.out.println("tm中第一个键值对:"+tm. firstEntry ());
System.out.println("tm中最后一个键值对:"+tm.lastEntry());
System.out.println("tm中最后一个键值对:"+tm. lastKey ());
System.out.println("tm中比2大的最小key:"+tm.higherKey (2));
System.out.println("tm中比3小的最大键值对:"+tm. lowerEntry (3));
System.out.println ("tm的子集合"+tm.subMap(2,4));
}
}

【例13_18】的运行结果如图13-25所示。​

第十三章《集合》第5节:Map集合_Java_06

图13-25【例13_18】运行结果​

13.5.5 EnumMap类

EnumMap是一个专门与枚举类一起使用的Map实现类,EnumMap中的所有key都必须是某个枚举的枚举值。创建 EnumMap时必须指定它对应的枚举类型。EnumMap具有如下特征。​

  • EnumMap在内部以数组形式保存,所以这种实现形式非常紧凑、高效​
  • EnumMap以key的自然顺序,也就是枚举值在枚举中的定义顺序来维护key-value对的顺序​
  • 当程序通过keySet()、entrySet()、values()等方法遍历EnumMap时可以看到这种顺序​
  • EnumMap不允许使用null 作为 key,但允许使用null 作为value​

与创建普通的Map有所区别的是,创建EnumMap时必须指定一个枚举类,从而将该EnumMap和指定枚举类关联起来。下面的【例13_19】展示了EnumMap类的使用方法。​

【例13_19 EnumMap类的使用】

Exam19.java​

import java.util.EnumMap;
enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
public class Exam13_19 {
public static void main(String[] args) {
//创建EnumMap时需要指定key的枚举类型
EnumMap map = new EnumMap(Season.class);
map.put(Season.AUTUMN,"秋凉");
map.put(Season.SUMMER,"夏热");
map.put(Season.SPRING,"春暖");
map.put(Season.WINTER,"冬冷");
System.out.println("map中的键值对:"+map);
}
}

【例13_19】的运行结果如图13-25所示。​

第十三章《集合》第5节:Map集合_键值对_07

图13-25【例13_19】运行结果​

从图13-25可以看出:EnumMap中的key-value对以key在枚举中定义的顺序排序。​

13.5.6各种Map实现类性能分析

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

TreeMap通常比HashMap、Hashtable要慢,尤其在插入、删除 key-value对时更慢,因为TreeMap底层采用红黑树来管理key-value对。​

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

对于一般的应用场景,程序应该多考虑使用因为HashMap底层其实也是采用数组来存储key-value对的。但如果程序需要一个总是排好序的Map时,则可以考虑使用TreeMap。​

LinkedHashMap比HashMap慢一点,因为它需要维护链表来保持Map中 key-value时的添加顺序。EnumMap的存储速度最快,但它只能使用同一个枚举类的枚举值作为key,因此有一定局限性。

本文字版教程还配有更详细的视频讲解,小伙伴们可以点击这里观看。

标签:Map,13,第十三章,map,value,key,集合
From: https://blog.51cto.com/mugexuetang/5983838

相关文章

  • 第十三章《集合》第6节:使用Collections类操作集合
    ​Java提供了一个操作集合的工具类Collections,这个类中提供了大量方法对集合元素进行排序、查询和修改等操作,此外还提供了将集合对象设置为不可变、对集合对象实现同步控制......
  • 第十三章《集合》第4节:Queue集合
    ​Queue是一个接口,它也是Collection接口的子接口。Queue用来模拟队列这种数据结构。队列这种数据结构最明显的特征是元素先入先出,队列的头部的元素是所有元素中最先进入队列......
  • 第十三章《集合》第2节:List集合
    ​List这个单词意为“列表”,List类型的集合的特点是:元素呈线性排列,致密且有序。下面的图13-3展示了List类型集合的特点。图13-3List类型集合​图13-3中的每一个小圆形代表......
  • 第十三章《集合》第3节:Set集合
    ​Set也是Collection的子接口,它定义了另一种形式的集合,专业上称之为Set集合。Set集合的特点如图13-9所示。图13-9Set类型集合​从图13-9可以看出:Set类型的集合就像是一个装......
  • QPixmap QImage
    QPixmap  QImage voidScrollBox::setThumbnail(QPixmapimg){QPixmapbkPixmap=img.fromImage(img.toImage().scaled(size(),Qt::IgnoreAspectRatio,Qt......
  • Java Map 集合类简介
    java.util中的集合类包含Java中某些最常用的类。最常用的集合类是List和Map。List的具体实现包括ArrayList和Vector,它们是可变大小的列表,比较适合构建、存储和......
  • 第十三章《集合》第1节:Java集合概述
    ​实际开发过程中,常常需要集中存放多个数据,虽然程序员可以使用数组来保存多个对象,但数组长度不可变化,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的,如果需要保......
  • rmap反向映射
    数据结构AV&AVC&VMAstructanon_vma{//AV是perVMA的structanon_vma*root;//指向祖宗(root)进程的anon_vmastructanon_vma*parent;//指向父进程的......
  • 18、前端基础-ES6---数组的map&reduce
    ......
  • java中的HashMap类的遍历
    示例代码如下:1publicclassHashMapBianLiTest{2publicstaticvoidmain(String[]args){3//hashMap的遍历4HashMaphashMap=new......