首页 > 代码库 > 集合框架之Map接口
集合框架之Map接口
Map是将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
Map 接口提供三种collection视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。
所有通用的映射实现类应该提供两个“标准的”构造方法:一个 void(无参数)构造方法,用于创建空映射;一个是带有单个 Map 类型参数的构造方法,用于创建一个与其参数具有相同键-值映射关系的新映射。实际上,后一个构造方法允许用户复制任意映射,生成所需类的一个等价映射。尽管无法强制执行此建议(因为接口不能包含构造方法),但是 JDK 中所有通用的映射实现都遵从它。
Map接口的方法列表如下:
void clear()
从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。
boolean equals(Object o)
比较指定的对象与此映射是否相等。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
int hashCode()
返回此映射的哈希码值。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回true。
Set<K> keySet()
返回此映射中包含的键的 Set 视图。
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V>m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size()
返回此映射中的键-值映射关系数。
Collection<V> values()
返回此映射中包含的值的Collection 视图。
Map有三个通用实现类:HashMap,TreeMap和LinkedHashMap,它们的行为及性能上的对比,可以参照Set接口中的HashSet,TreeSet,LinkedHashSet,十分类似
下面我们通过三个实例来看一下这三种实现类的区别:
String[] message = {"if", "it", "is", "to","be", "it", "is", "up", "to","me", "to", "delegate"}; Map<String,Integer> map = newHashMap<String,Integer>(); for(String str : message){ Integer freq = map.get(str); map.put(str, freq == null ? 1 : freq+1); } System.out.println("单词总数:"+map.size()); System.out.println(map);控制台输出:
单词总数:8
{to=3, is=2,it=2, if=1, me=1, delegate=1, up=1, be=1}
该实例使用HashMap作为容器,形成单词和词频的映射
假如我们想让Map中的元素按照字母顺序排序,把实现类换成TreeMap即可{"if", "it", "is", "to","be", "it
String [] message = {"if", "it", "is", "to","be", "it", "is", "up", "to","me", "to", "delegate"}; Map<String,Integer> map = new TreeMap<String,Integer>(); for(String str : message){ Integer freq = map.get(str); map.put(str, freq == null ? 1 : freq+1); } System.out.println("单词总数:"+map.size()); System.out.println(map);控制台输出:
单词总数:8
{be=1, delegate=1, if=1, is=2,it=2, me=1, to=3, up=1}
类似地,如果我们想让打印结果按照单词第一次出现的顺序排序,将实现类改成LinkedHashMap即可
String [] message = {"if", "it", "is", "to","be", "it", "is", "up", "to","me", "to", "delegate"}; Map<String,Integer> map = newTreeMap<String,Integer>(); for(String str : message){ Integer freq = map.get(str); map.put(str, freq == null ?1 : freq+1); } System.out.println("单词总数:"+map.size()); System.out.println(map);控制台输出:
单词总数:8
{if=1, it=2,is=2, to=3, be=1, up=1, me=1, delegate=1}
在上面的三个实例中,我们只改变了实现类的名称,其他代码一点都没做改动,就达到了想要的效果,这就是面向接口编程赋予程序的弹性。
Map提供了三种转换为Collection视图(可以将Map当做Collection来看)的方法:
keySet():Map中的键Set型集合
values():Map中的值Collection集合,不能为Set,值集合中可能会有重复值
entrySet():Map中的键值对Set型集合
Map的Collection视图提供了唯一的Map的迭代方法。Map并没有提供返回Iterator迭代器的方法,只能借助于Collection视图的迭代器来遍历Map
需要注意的是,entrySet()返回的Set里面的元素是Map.Entry<K,V>,该接口提供了一些方法来操作键值对,方法列表如下:
boolean equals(Object o)
比较指定对象与此项的相等性。
K getKey()
返回与此项对应的键。
V getValue()
返回与此项对应的值。
int hashCode()
返回此映射项的哈希码值。
V setValue(V value)
用指定的值替换与此项对应的值(可选操作)。
Map.entrySet 方法返回映射的collection 视图,其中的元素属于此类。获得映射项引用的唯一方法是通过此collection 视图的迭代器来实现。这些Map.Entry 对象仅在迭代期间有效;更确切地讲,如果在迭代器返回项之后修改了底层映射,则某些映射项的行为是不确定的,除了通过 setValue 在映射项上执行操作之外。
下面给出具体的实例:
String [] message = {"if", "it", "is", "to","be", "it", "is", "up", "to","me", "to", "delegate"}; Map<String,Integer> map = newHashMap<String,Integer>(); for(String str : message){ Integer freq = map.get(str); map.put(str, freq == null ?1 : freq+1); } for(String key : map.keySet()){ System.out.println(key+"——》"+map.get(key)); }
该实例通过遍历键集合打印出所有的键值对还可以通过遍历entrySet集合实现上面的过程,下面的代码与上面的for-each循环是等价的
for(Map.Entry<String,Integer>entry : map.entrySet()){ System.out.println(entry.getKey()+"——》"+entry.getValue()); }HashMap
基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap 的实例有两个参数影响其性能:初始容量和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
通常,默认加载因子 (0.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。
扩展的构造方法(即除了Map实现类都有的一个无参的构造方法和一个以Map为参数的构造方法之外,自身独有的构造方法)
Hashtable(intinitialCapacity)
用指定初始容量和默认的加载因子(0.75) 构造一个新的空哈希表。
Hashtable(intinitialCapacity, float loadFactor)
用指定初始容量和指定加载因子构造一个新的空哈希表。 HashMap实现了Map接口的所有方法,没有自身特有的扩展方法
TreeMap
基于红黑树(Red-Black tree)同时继承了NavigableMap接口的实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
注意,如果要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须与 equals 一致。(关于与 equals 一致的精确定义,请参阅 Comparable 或 Comparator)。这是因为 Map 接口是按照 equals 操作定义的,但有序映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然是定义良好的,只不过没有遵守 Map 接口的常规协定。
TreeMap的扩展的构造方法:
TreeMap(Comparator<?super K> comparator)
构造一个新的、空的树映射,该映射根据给定比较器进行排序。
TreeMap(SortedMap<K,?extends V> m)
构造一个与指定有序映射具有相同映射关系和相同排序顺序的新的树映射。
扩展的方法:
Comparator<?super K> comparator()
返回对此映射中的键进行排序的比较器;如果此映射使用键的自然顺序,则返回 null。
NavigableSet<K> descendingKeySet()
返回此映射中所包含键的逆序NavigableSet 视图。
NavigableMap<K,V> descendingMap()
返回此映射中所包含映射关系的逆序视图。
Map.Entry<K,V> firstEntry()
返回一个与此映射中的最小键关联的键-值映射关系;如果映射为空,则返回 null。
K firstKey()
返回此映射中当前第一个(最低)键。
Map.Entry<K,V> floorEntry(K key)
返回一个键-值映射关系,它与小于等于给定键的最大键关联;如果不存在这样的键,则返回 null。
K floorKey(K key)
返回小于等于给定键的最大键;如果不存在这样的键,则返回 null。
SortedMap<K,V> headMap(K toKey)
返回此映射的部分视图,其键值严格小于toKey。
NavigableMap<K,V> headMap(K toKey,boolean inclusive)
返回此映射的部分视图,其键小于(或等于,如果 inclusive 为 true)toKey。
Map.Entry<K,V> higherEntry(K key)
返回一个键-值映射关系,它与严格大于给定键的最小键关联;如果不存在这样的键,则返回 null。
K higherKey(K key)
返回严格大于给定键的最小键;如果不存在这样的键,则返回 null。
Map.Entry<K,V> lastEntry()
返回与此映射中的最大键关联的键-值映射关系;如果映射为空,则返回 null。
K lastKey()
返回映射中当前最后一个(最高)键。
Map.Entry<K,V> lowerEntry(K key)
返回一个键-值映射关系,它与严格小于给定键的最大键关联;如果不存在这样的键,则返回 null。
K lowerKey(K key)
返回严格小于给定键的最大键;如果不存在这样的键,则返回 null。
NavigableSet<K> navigableKeySet()
返回此映射中所包含键的NavigableSet 视图。
Map.Entry<K,V> pollFirstEntry()
移除并返回与此映射中的最小键关联的键-值映射关系;如果映射为空,则返回 null。
Map.Entry<K,V> pollLastEntry()
移除并返回与此映射中的最大键关联的键-值映射关系;如果映射为空,则返回 null。
NavigableMap<K,V> subMap(K fromKey,boolean fromInclusive, K toKey, boolean toInclusive)
返回此映射的部分视图,其键的范围从fromKey 到 toKey。
SortedMap<K,V> subMap(K fromKey, K toKey)
返回此映射的部分视图,其键值的范围从fromKey(包括)到 toKey(不包括)。
SortedMap<K,V> tailMap(K fromKey)
返回此映射的部分视图,其键大于等于fromKey。
NavigableMap<K,V> tailMap(K fromKey,boolean inclusive)
返回此映射的部分视图,其键大于(或等于,如果 inclusive 为 true)fromKey。
LinkedHashMap
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入键,则插入顺序不受影响。(如果在调用 m.put(k, v) 前 m.containsKey(k) 返回了 true,则调用时会将键 k 重新插入到映射 m 中。)
可以重写 removeEldestEntry(Map.Entry)方法来实施策略,以便在将新映射关系添加到映射时自动移除旧的映射关系。
此类提供所有可选的 Map 操作,并且允许 null 元素。与 HashMap 一样
扩展的构造方法:
LinkedHashMap(intinitialCapacity)
构造一个带指定初始容量和默认加载因子(0.75) 的空插入顺序 LinkedHashMap 实例。
LinkedHashMap(intinitialCapacity, float loadFactor)
构造一个带指定初始容量和加载因子的空插入顺序 LinkedHashMap 实例。
LinkedHashMap(intinitialCapacity, float loadFactor, boolean accessOrder)
构造一个带指定初始容量、加载因子和排序模式的空 LinkedHashMap 实例。
扩展的方法:
protected boolean removeEldestEntry(Map.Entry<K,V> eldest)
如果此映射移除其最旧的条目,则返回true。
Properties
虽然Properties类是Map的子类,但是它并没有实现Map的方法,它与上面的其他子类有本质的区别,由于在应用中广泛使用property 作为配置文件,在此一并列出并给出代码实例
Properties 类表示了一个持久的属性集。Properties可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
一个属性列表可包含另一个属性列表作为它的“默认值”;如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性列表。
因为 Properties 继承于 Hashtable,所以可对Properties 对象应用 put 和 putAll 方法。但不建议使用这两个方法,因为它们允许调用者插入其键或值不是 String 的项。相反,应该使用 setProperty 方法。如果在“不安全”的 Properties 对象(即包含非 String 的键或值)上调用 store 或 save 方法,则该调用将失败。类似地,如果在“不安全”的 Properties 对象(即包含非 String 的键)上调用 propertyNames 或 list 方法,则该调用将失败。
此类是线程安全的:多个线程可以共享单个Properties 对象而无需进行外部同步。
load(Reader) /store(Writer, String) 方法按下面所指定的、简单的面向行的格式在基于字符的流中加载和存储属性。除了输入/输出流使用 ISO 8859-1 字符编码外,load(InputStream) / store(OutputStream, String) 方法与load(Reader)/store(Writer, String) 对的工作方式完全相同。可以使用Unicode 转义来编写此编码中无法直接表示的字符;转义序列中只允许单个 ‘u‘ 字符。可使用 native2ascii 工具对属性文件和其他字符编码进行相互转换。
loadFromXML(InputStream)和 storeToXML(OutputStream, String, String) 方法按简单的 XML 格式加载和存储属性。默认使用 UTF-8 字符编码,但如果需要,可以指定某种特定的编码。XML 属性文档具有以下 DOCTYPE 声明:
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
注意,导入或导出属性时不访问系统 URI (http://java.sun.com/dtd/properties.dtd);该系统 URI 仅作为一个唯一标识 DTD的字符串:
<?xml version="1.0"encoding="UTF-8"?> <!-- DTD for properties --> <!ELEMENT properties ( comment?, entry*) > <!ATTLIST properties version CDATA#FIXED "1.0"> <!ELEMENT comment (#PCDATA) > <!ELEMENT entry (#PCDATA) > <!ATTLIST entry key CDATA #REQUIRED>方法列表:
String getProperty(String key)
用指定的键在此属性列表中搜索属性。
String getProperty(String key, StringdefaultValue)
用指定的键在属性列表中搜索属性。
void list(PrintStream out)
将属性列表输出到指定的输出流。
void list(PrintWriter out)
将属性列表输出到指定的输出流。
void load(InputStream inStream)
从输入流中读取属性列表(键和元素对)。
void load(Reader reader)
按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
void loadFromXML(InputStream in)
将指定输入流中由 XML 文档所表示的所有属性加载到此属性表中。
Enumeration<?> propertyNames()
返回属性列表中所有键的枚举,如果在主属性列表中未找到同名的键,则包括默认属性列表中不同的键。
Object setProperty(String key, String value)
调用 Hashtable 的方法 put。
void store(OutputStream out, String comments)
以适合使用load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
void store(Writer writer, String comments)
以适合使用 load(Reader) 方法的格式,将此Properties 表中的属性列表(键和元素对)写入输出字符。
void storeToXML(OutputStream os, Stringcomment)
发出一个表示此表中包含的所有属性的 XML文档。
void storeToXML(OutputStream os, Stringcomment, String encoding)
使用指定的编码发出一个表示此表中包含的所有属性的 XML 文档。
Set<String> stringPropertyNames()
返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。
代码实例:
private staticFile filePath = new File("d:\\test.properties"); //读取所有的属性 public static void readAllProps(){ Properties props = newProperties(); try{ InputStream in = newBufferedInputStream(new FileInputStream(filePath)); props.load(in); Enumeration en =props.propertyNames(); System.out.println("property文件的所有属性如下:"); while(en.hasMoreElements()){ String key =(String)en.nextElement(); String value =http://www.mamicode.com/props.getProperty(key);>
集合框架之Map接口