Map集合
Map集合
Map集合用于保存映射关系的数据,Map集合中保存了两组值,一组是 key, 一组是 value。
Map的key不能重复。
key和value之间存在单向一对一的关系, 通过key,能找到唯一的,确定的value。
public interface Map<K,V> {
// Query Operations
}
Map的key放在一起来看,组成了一个Set集合,value又类似于一个List集合。
Map接口中定义了如下常用方法:
- clear():删除Map中所有的 key-value对
- containsKey():是否包含指定的key
- containsValue(): 是否包含一个或多个value。
- entrySet(): 返回包含的 key-value对组成的Set集合,每个集合元素都是Map.Entry对象, Entry是Map的内部类。
- get():返回指定key对应的value。
- isEmpty(): 是否为空
- keySet(): 返回key组成的Set集合
- put(): 添加一个key-value对,如果存在,覆盖以前的
- putAll(): 指定Map中复制过来
- remove():删除指定key,或 key-value。
- size():返回个数
- values():返回所有 value组成的 Collection。
JAVA 8 新增方法: - compute(): 根据第二个参数重新计算,并设置value
- computeIfAbsent(): 同上,value为 Null时计算
- computeIfPresent(): 同上, value不为Null时计算
- forEach(): 遍历Map
- merge(): value为null时,用传入的value覆盖,不为null时,重新计算覆盖
- putIfAbsent(): value为null时覆盖
- replace():key不存在时,不添加
- replaceAll(): 重新计算key-value, 覆盖value。
Map中包含一个内部接口 Entry。
封装了一个key-value对。包含下面方法。
- getKey(): 返回key
- getValue(): 返回value
- setValue(): 重新设置value
Map map = new HashMap();
map.put("A", 100);
map.put("B", 90);
map.put("C", 110);
System.out.println(map);
//D 不存在,不会改变
map.replace("D", 80);
System.out.println(map);
//重新计算A
map.merge("A", 20, (oldVal, newVal) -> (Integer)oldVal + (Integer) newVal);
System.out.println(map);
//遍历
map.forEach((key, value) -> System.out.println("key : " + key + " , value:" + value));
//D 不存在,不存在时计算
map.computeIfAbsent("D", (key) -> ((String) key).length());
System.out.println(map);
//D存在了, 存在计算
map.computeIfPresent("D", (key, value) -> ((String) key).length() * 10);
System.out.println(map);
//Output
{A=100, B=90, C=110}
{A=100, B=90, C=110}
{A=120, B=90, C=110}
key : A , value:120
key : B , value:90
key : C , value:110
{A=120, B=90, C=110, D=1}
{A=120, B=90, C=110, D=10}
HashMap和Hashtable
HashMap和Hashtable是Map接口的实现类。
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
}
Hashtable是古老的Map实现类,命名上都没有遵守JAVA命名规范,单词首字母大写。
JAVA 8改进了HashMap的实现,使用HashMap存在key冲突时也有较好的性能。
Hashtable 和 HashMap区别:
Hashtable 是线程安全的, HashMap是线程不安全的。
Hashtable 使用 null作为key和value, HashMap可以使用null作为key或value。 由于key不能重复, 最多只能有一个key为null。
Hashtable 比较古老,性能较差, 不推荐使用,
为了线程安全,可以使用 Collections 工具类。
HashMap hashMap = new HashMap();
hashMap.put("a", null);
hashMap.put(null, null);
hashMap.put(null, null);
System.out.println(hashMap);
//Output
{null=null, a=null}
HashMap 同 HashSet, 判断两个key相等标准也是: 两个key通过 equals方法返回true, hashCode()也相等。
同HashSet, 可变对象最为HashMap的key, 程序如果修改了对象,程序无法准确的访问Map中的被修改的key。
class AA{
int count;
public AA(int count){
this.count = count;
}
@Override
public boolean equals(Object obj) {
if(obj == this)
return true;
if(obj != null && obj.getClass() == AA.class){
AA a = (AA) obj;
return a.count == this.count;
}
return false;
}
@Override
public int hashCode() {
return this.count;
}
}
public class MapTest {
public static void main(String[] args){
HashMap hashMap = new HashMap();
hashMap.put(new AA(100), "A");
hashMap.put(new AA(200), "B");
Iterator iterator = hashMap.keySet().iterator();
AA aa = (AA) iterator.next();
aa.count = 200;
System.out.println(hashMap);
//只能删除没有被修改过key的
hashMap.remove(new AA(200));
System.out.println(hashMap);
//无法获取剩下的value
System.out.println(hashMap.get(new AA(200)));
System.out.println(hashMap.get(new AA(100)));
}
}
//Output
{testCode.AA@c8=A, testCode.AA@c8=B}
{testCode.AA@c8=A}
null
null
尽量不要使用可变对象作为 HashMap, Hashtable 的key, 如果使用,尽量不要修改作为key的可变对象。
LinkedHashMap实现类
LinkedHashMap 是 HashMap的子类。
LinkedHashMap使用双向链表维护key-value的次序。使迭代顺序和插入顺序一致。
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
}
LinkedHashMap linkedHashMap = new LinkedHashMap();
linkedHashMap.put("A", 100);
linkedHashMap.put("B", 200);
linkedHashMap.put("C", 300);
linkedHashMap.forEach((key, value) -> System.out.println(key + " ---> " + value));
//Output
A ---> 100
B ---> 200
C ---> 300
Properties
Properties 是 Hashtable 的子类。
public
class Properties extends Hashtable<Object,Object> {
}
可以方便的处理属性文件。
可以把属性文件,如 ini 中 属性名=属性值
加载到Map对象中。
Properties 相当于 key, value都是 String类型的Map。
主要有以下方法:
- getProperty(): 获取属性值, 可以有 defaultValue。
- setProperty(): 设置属性值
- load():从属性文件中加载到 Properties里
- store():输出到指定文件
下面是使用:
Properties properties = new Properties();
properties.setProperty("name", "liu");
properties.setProperty("age", "20");
System.out.println(properties);
properties.store(new FileOutputStream("p.ini"), "comment");
Properties properties1 = new Properties();
properties1.load(new FileInputStream("p.ini"));
System.out.println(properties1);
//Output
{age=20, name=liu}
{age=20, name=liu}
根目录下生成 p.ini文件
#comment
#Wed Feb 06 18:24:46 CST 2019
age=20
name=liu
TreeMap
TreeMap 是 SortedMap接口的实现类。
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
}
public interface NavigableMap<K,V> extends SortedMap<K,V> {
}
public interface SortedMap<K,V> extends Map<K,V> {
}
TreeMap是根据 key来排序, 就是一个红黑树数据结构,每个key-value对是一个红黑树节点。保证所有的key-value对处于有序状态。
同 TreeSet , 也有两种排序方法:
自然排序
定制排序。
排序判断标准是根据 compareTo() 方法的返回值。
主要方法包括: 取第一个, 最后一个, 前一个,后一个等 key-value对。 Map.Entry。
WeakHashMap
WeakHashMap 是 Map的实现类, 与HashMap基本相似.
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {
}
HashMap的key保留了对实际对象的强引用,只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾收集。
WeakHashMap 的key只保留了对实际对象的弱引用,该对象的key对所引用的对象没有被其他强引用对象所引用, 则key所引用的对象可能被垃圾回收,WeakHashMap也可能自动删除这些key所对应的key-value对。
WeakHashMap weakHashMap = new WeakHashMap();
//添加三个匿名字符串对象
weakHashMap.put(new String("A"), 1);
weakHashMap.put(new String("B"), 2);
weakHashMap.put(new String("C"), 3);
//添加key 系统缓存的字符串对象
weakHashMap.put("java", 4);
System.out.println(weakHashMap);
System.gc();
System.runFinalization();
System.out.println(weakHashMap);
//Output
{java=4, C=3, B=2, A=1}
{java=4}
对于前三个匿名的字符串对象,WeakHashMap 保留了对他们的弱引用。垃圾回收时自动删除了这三个。
对于第四个 key是一个字符串直接量,系统自动保留了强引用,所以垃圾回收时不会回收它。
IdentityHashMap
IdentityHashMap是Map的实现类, 与HashMap基本相似.
他对处理两个 key相等时比较独特,当两个key严格相等时(key1 == key2).IdentityHashMap才认为两个key相等。
对于普通HashMap, 只要 equals返回TRUE, 且 hashCode值相等即可。
public class IdentityHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, java.io.Serializable, Cloneable
{
}
测试例子:
IdentityHashMap identityHashMap = new IdentityHashMap();
identityHashMap.put(new String("A"), 1);
identityHashMap.put(new String("A"), 2);
identityHashMap.put("B", 3);
identityHashMap.put("B", 4);
System.out.println(identityHashMap);
//Output
{A=2, A=1, B=4}
前两个是新创建的字符串对象, == 为false,
后两个是字符串直接量,JAVA使用常量池管理字符串直接量, == 为true
EnumMap
EnumMap是与枚举类一起使用的Map。
key必须是单个枚举类的枚举值。
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
implements java.io.Serializable, Cloneable
{
}
- EnumMap内部以数组形式保存,这种实现非常紧凑,高效
- EnumMap根据key在枚举类中的定义的顺序,维护key-value顺序。
- 不允许 null 作为key
下面示例:
enum GenderEnum{
MALE,FEMALE;
}
public class MapTest {
public static void main(String[] args) throws Exception{
EnumMap enumMap = new EnumMap(GenderEnum.class);
enumMap.put(GenderEnum.FEMALE, "女人");
enumMap.put(GenderEnum.MALE, "男人");
System.out.println(enumMap);
}
}
//Output
{MALE=男人, FEMALE=女人}
FEMALE 先put, 但是输出还是按照枚举类中的定义顺序输出。
Map性能分析
HashMap 和 Hashtable 的实现机制几乎一样, Hashtable是一个古老的,线程安全的集合,因此 HashMap通常比Hashtable 性能好。
TreeMap是红黑树管理key-value对,一直是排序状态,性能比HashMap要慢。
LinkedHashMap比HashMap要慢, 需要链表来保持Map中的添加顺序。
EnumMap性能最好,使用同一个枚举类的枚举值为key。
性能选项
HashMap, HashSet 等通过 hash 算法决定Map中 key的存储。
hash 表里的存储元素的位置称为 桶,bucket。
- 容量 capacity: hash表中桶的数量
- 初始化容量 initial capacity: 创建 hash表时桶的数量
- 尺寸 size: 当前 hash表中记录的数量
- 负载因子(load factor):size/capacity. 0表示空hash表, 0.5表示半满的 hash表
负载极限,表示hash表的最大填满程度,当负载因子达到负载极限时,进行 rehashing。 hash表会成倍的增加容量,桶的数量, 将原有的对象重新分配。
负载极限默认值为 0.75。
地址: https://blog.youkuaiyun.com/yonggang7/article/details/86768876