JDK8源码详解-util篇-AbstractMap

JDK8源码详解-util篇-AbstractMap,第1张

概述

功能:该类提供了Map接口的基础框架

开始版本:JDK 1.2

注意:
1. 本类为抽象
2. 使用key-value键值对储存,可以存在相同的value,不能存在相同的keykeyvalue均可为null
3. 数据结构:Map的数据结构种类较多,不同实现有不同的数据结构,大致包括顺序表、链表、树、散列表

备注:本类没有实现AbstractCollection,内部没有迭代器,因此Map不能直接使用迭代器循环

实现接口:java.util.Map

所在包:java.util

导入类:java.util.Map.Entry

类声明:

public abstract class AbstractMap<K,V> implements Map<K,V> {}

框架图:

变量 包变量 01.key的集合 keySet

补充:transient —— 标识此变量不进行序列化的关键字,当使用Serializable序列化时不会被序列化(静态变量除外),使用Exteranlizable序列化时根据重写的方法决定时进行序列化

// key的集合,Set类型,不可重复
transient Set<K> keySet;
02.value的集合 values
// value的集合,Collection类型
transient Collection<V> values;
方法 构造器
// 唯一构造器
protected AbstractMap() {}
内部类 01.可变的键值对 SimpleEntry

注意:
1. 本方法存在于1.6及以后版本
2. 提供给实现类使用,本类未使用此内部类
3. 本方法不提供修改key值的方法

// 源码
public static class SimpleEntry<K,V> implements Entry<K,V>, java.io.Serializable
{
    private static final long serialVersionUID = -8499721149061103585L;
    
    // key value 变量
    private final K key;
    private V value;
    
    // 构造器,传key-value 
    public SimpleEntry(K key, V value) {
        this.key   = key;
        this.value = value;
    }
    
    // 构造器,传键值对对象
    public SimpleEntry(Entry<? extends K, ? extends V> entry) {
        this.key   = entry.getKey();
        this.value = entry.getValue();
    }
    
    // 获取key值
    public K getKey() {
        return key;
    }

    // 获取value值
    public V getValue() {
        return value;
    }

    // 设置value,返回旧value值
    public V setValue(V value) {
        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }

    // 比较,调用eq方法进行key和value内容的比较
    public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        return eq(key, e.getKey()) && eq(value, e.getValue());
    }

    // 哈希值
    public int hashCode() {
        return (key   == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
    }

    // 输出字符串,格式:key=value
    public String toString() {
        return key + "=" + value;
    }

}
02.不可变的键值对 SimpleImmutableEntry

注意:
1. 本方法存在于1.6及以后版本
2. 提供给实现类使用,本类未使用此内部类
3. 本方法不提供修改key值的方法
4. 与SimpleEntry仅有setValue方法不同,使用本内部类时若修改value会抛出UnsupportedOperationException异常

// 源码
public static class SimpleImmutableEntry<K,V> implements Entry<K,V>, java.io.Serializable
{
    private static final long serialVersionUID = 7138329143949025153L;
    
    // key value 变量
    private final K key;
    private final V value;

    // 构造器,传key-value
    public SimpleImmutableEntry(K key, V value) {
        this.key   = key;
        this.value = value;
    }

    // 构造器,传键值对对象
    public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
        this.key   = entry.getKey();
        this.value = entry.getValue();
    }

    // 获取key值
    public K getKey() {
        return key;
    }

    // 获取value值
    public V getValue() {
        return value;
    }

    // 设置value值,本内部类是不可变键值对,因此调用本方法会抛出异常
    public V setValue(V value) {
        throw new UnsupportedOperationException();
    }

    // 比较,调用eq方法进行key和value内容的比较
    public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        return eq(key, e.getKey()) && eq(value, e.getValue());
    }

    // 哈希值
    public int hashCode() {
        return (key   == null ? 0 :   key.hashCode()) ^
               (value == null ? 0 : value.hashCode());
    }

    // 输出字符串,格式:key=value
    public String toString() {
        return key + "=" + value;
    }

}
抽象方法 01.获取键值对映射关系的Set对象 entrySet()

注意:
1. Set的元素是唯一的
2. 一般可以使用此方法进行遍历,可以使用此方法将Map转换为的Set集合再使用迭代器循环

// 源码
public abstract Set<Entry<K,V>> entrySet();
// HashMap示例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    // 此处直接返回的是一个Set,元素类型是Map.Entry<>
    Set<Map.Entry<String, Object>> entries = map.entrySet();
    // [one=1, two=2, three=3]
    System.out.println(entries);
}
保护方法 01.克隆 clone()

注意:本方法需要重写,调用了Objectclone()

// 源码
protected Object clone() throws CloneNotSupportedException {
    AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
    // 直接调用了Object的clone(),无实际实现,设置key集合和value集合为null
    result.keySet = null;
    result.values = null;
    return result;
}
私有方法 01.比较两个对象是否一致 eq(Object o1, Object o2)

参数:
1. o1 —— 要比较的对象1
2. o2 —— 要比较的对象2

注意:此方法提供给内部类使用

// 源码
private static boolean eq(Object o1, Object o2) {
    return o1 == null ? o2 == null : o1.equals(o2);
}
公有方法 01.获取Map的元素个数 size()

注意:此处是将Map转换为Set后计算的个数

// 源码
public int size() {
    return entrySet().size();
}
02.Map是否为空 isEmpty()

注意:此处是将Map转换为Set后计算的个数再进行判空的

// 源码
public boolean isEmpty() {
    return size() == 0;
}
03.当前Map的value中是否包含某一元素 containsValue(Object value)

参数:value —— 要判断的value

注意:
1. 指定元素为null需要单独比较,使用equals会出现空指针
2. 调用getValue()获取值

// 源码
public boolean containsValue(Object value) {
    // 通过转换为的Set集合使用迭代器遍历
    Iterator<Entry<K,V>> i = entrySet().iterator();
    if (value==null) {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getValue()==null)
                return true;
        }
    } else {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (value.equals(e.getValue()))
                return true;
        }
    }
    return false;
}
// 示例:使用HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    // true
    System.out.println(map.containsValue(4));
}
04.当前Map的key中是否包含某一元素 containsKey(Object key)

参数:key —— 要判断的key

注意:
1. 指定元素为null需要单独比较,使用equals会出现空指针
2. 调用getKey()获取Key

// 源码
public boolean containsKey(Object key) {
    // 通过转换为的Set集合使用迭代器遍历
    Iterator<Map.Entry<K,V>> i = entrySet().iterator();
    if (key==null) {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getKey()==null)
                return true;
        }
    } else {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (key.equals(e.getKey()))
                return true;
        }
    }
    return false;
}
// 示例:使用HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    // false
    System.out.println(map.containsKey("six"));
}
05.根据key获取对应的value值 get(Object key)

参数:key —— 要获取的键值对的key

注意:
1. 指定元素为null需要单独比较,使用equals会出现空指针
2. 本方法实际是从头循环查找指定key的,根据实现类的底层数据结构不同时间复杂度等各不同
3. 需注意查找不到指定key返回的是null,查询指定key的值若为null,无法区别这两种情况,因此本方法不建议使用本方法判断Map中是否包含某个key,建议使用containsKey(Object key)

// 源码
public V get(Object key) {
    // 通过转换为的Set集合使用迭代器遍历
    Iterator<Entry<K,V>> i = entrySet().iterator();
    if (key==null) {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getKey()==null)
                return e.getValue();
        }
    } else {
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            if (key.equals(e.getKey()))
                return e.getValue();
        }
    }
    return null;
}
// 示例:使用HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    // null
    System.out.println(map.get("six"));
    // null
    System.out.println(map.get("zero"));
}
06.存入Map键值对 put(K key,V value)

参数:
1. key —— 要存入的key
2. value —— key对应的值

注意:
1. 此方法必须重写,否则会直接抛出UnsupportedOperationException异常
2. Map是否有序根据具体实现类的数据结构决定

// 源码
public V put(K key, V value) {
    throw new UnsupportedOperationException();
}
// 示例:HashMap实例
@Test
void contextLoads4() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("ooo",1.25);
    map.put("four",4);
    map.put("zero",null);
    // HashMap无序:{zero=null, four=4, one=1, two=2, three=3}
    System.out.println(map);
}
07.移除指定key值的键值对 remove(Object key)

参数:key —— 要移除的key

注意:
1. 本方法使用了迭代器,生成的Set集合是动态的,会受Map集合影响,在生成迭代器后直接对Map进行 *** 作仍会导致迭代器的ConcurrentModificationException异常
2. 指定元素为null需要单独比较,使用equals会出现空指针
3. 本方法返回的是移除的key对应的值,未找到元素则返回null,注意此处若移除的key的值为null也会返回null,无法根据返回值判断Map是否包含key

// 源码
public V remove(Object key) {
    Iterator<Entry<K,V>> i = entrySet().iterator();
    Entry<K,V> correctEntry = null;
    // 寻找指定key对应的键值对
    if (key==null) {
        // 找到指定key值并将键值对存入后退出循环
        while (correctEntry==null && i.hasNext()) {
            Entry<K,V> e = i.next();
            if (e.getKey()==null)
                correctEntry = e;
        }
    } else {
        while (correctEntry==null && i.hasNext()) {
            Entry<K,V> e = i.next();
            if (key.equals(e.getKey()))
                correctEntry = e;
        }
    }
    // 此处若找到了键值对,则将值储存下来并通过迭代器移除(此处未进行next()移动游标就退出了循环)
    V oldValue = null;
    if (correctEntry !=null) {
        oldValue = correctEntry.getValue();
        i.remove();
    }
    return oldValue;
}
// 异常示例:生成迭代器后新增键值对
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    Set<Map.Entry<String, Object>> entries = map.entrySet();
    Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
    map.put("five",5);
    while (iterator.hasNext()){
        // java.util.ConcurrentModificationException
        System.out.println(iterator.next());
    }
}
// 示例:HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    
    // {zero=null, four=4, one=1, two=2, three=3}
    System.out.println(map);
    
    Object zero = map.remove("zero");
    // null
    System.out.println(zero);
    
    Object six = map.remove("six");
    // null
    System.out.println(six);
    
    Object two = map.remove("two");
    // 2
    System.out.println(two);
    
    // {four=4, one=1, two=2, three=3}
    System.out.println(map);
}
08.将指定Map内容存入指定对象 putAll(Map m)

参数:m —— 指定的储存键值对的对象

注意:
1. 当指定Mapkey已存在于当前Map,覆盖之前key的值
2. 本方法实际是循环调用了put(K key, V value)方法,而put(K key, V value)方法需要重写
3. 本方法未使用迭代器循环,而是直接使用Set的增强for循环进行遍历的

// 源码
public void putAll(Map<? extends K, ? extends V> m) {
    for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
        put(e.getKey(), e.getValue());
}
09.清空全部键值对 clear()

注意:本方法调用的实际是Setclear()方法

// 源码
public void clear() {
    entrySet().clear();
}
// 示例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    // {zero=null, four=4, one=1, two=2, three=3}
    System.out.println(map);
    map.clear();
    // {}
    System.out.println(map);
}
10.获取key的集合 keySet()

注意:
1. 未调用过本方法,变量keySetnull,调用本方法时对keySet进行初始化并赋值
2. 本方法生成的集合仅有contains(Object k)方法和迭代器的next()方法实现不同,contains(Object k)方法调用的是containsKey(Object key)方法,next()方法调用的是getKey()

// 源码
public Set<K> keySet() {
    // 默认keySet未进行初始化为null,即ks为null
    Set<K> ks = keySet;
    if (ks == null) {
        // 初始化,根据当前Map创建key的集合
        ks = new AbstractSet<K>() {
            public Iterator<K> iterator() {
                // 创建一个普通的迭代器并初始化
                return new Iterator<K>() {
                    private Iterator<Entry<K,V>> i = entrySet().iterator();

                    public boolean hasNext() {
                        return i.hasNext();
                    }

                    public K next() {
                        return i.next().getKey();
                    }

                    public void remove() {
                        i.remove();
                    }
                };
            }

            public int size() {
                return AbstractMap.this.size();
            }

            public boolean isEmpty() {
                return AbstractMap.this.isEmpty();
            }

            public void clear() {
                AbstractMap.this.clear();
            }

            public boolean contains(Object k) {
                return AbstractMap.this.containsKey(k);
            }
        };
        // 在这里对keySet进行了赋值
        keySet = ks;
    }
    return ks;
}
// 示例:HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    // [zero, four, one, two, three]
    System.out.println(map.keySet());
}
11.获取value的集合 values()

注意:
1. 未调用过本方法,变量valuesnull,调用本方法时对values进行初始化并赋值
2. 本方法生成的集合仅有contains(Object k)方法和迭代器的next()方法实现不同,contains(Object k)方法调用的是containsValue(Object key)方法,next()方法调用的是getValue()
3. 本方法返回的是Collection集合,value可以重复

// 源码
public Collection<V> values() {
    // 默认values未进行初始化为null,即vals为null
    Collection<V> vals = values;
    if (vals == null) {
        vals = new AbstractCollection<V>() {
            public Iterator<V> iterator() {
                return new Iterator<V>() {
                    private Iterator<Entry<K,V>> i = entrySet().iterator();

                    public boolean hasNext() {
                        return i.hasNext();
                    }

                    public V next() {
                        return i.next().getValue();
                    }

                    public void remove() {
                        i.remove();
                    }
                };
            }

            public int size() {
                return AbstractMap.this.size();
            }

            public boolean isEmpty() {
                return AbstractMap.this.isEmpty();
            }

            public void clear() {
                AbstractMap.this.clear();
            }

            public boolean contains(Object v) {
                return AbstractMap.this.containsValue(v);
            }
        };
        values = vals;
    }
    return vals;
}
// 示例:HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    map.put("six",null);
    // [null, null, 4, 1, 2, 3]
    System.out.println(map.values());
}
12.比较 equals(Object o)

参数:o —— 比较的对象

// 源码
public boolean equals(Object o) {
    if (o == this)
        return true;

    if (!(o instanceof Map))
        return false;
    Map<?,?> m = (Map<?,?>) o;
    // 先进行个数的比较
    if (m.size() != size())
        return false;
    // 循环进行键值对的比较
    try {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            // 此处使用get()若不存在key也会返回null,因此需要多判断一个是否存在当前key值
            if (value == null) {
                if (!(m.get(key)==null && m.containsKey(key)))
                    return false;
            } else {
                if (!value.equals(m.get(key)))
                    return false;
            }
        }
    } catch (ClassCastException unused) {
        return false;
    } catch (NullPointerException unused) {
        return false;
    }

    return true;
}
// 示例1:HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    map.put("six",null);

    Map<String,Object> map2 = new HashMap<>();
    map2.put("one",1);
    map2.put("two",2);
    map2.put("three",3);
    map2.put("four",4);
    map2.put("zero",null);

    // false
    System.out.println(map.equals(map2));
}
// 示例2:HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);

    Map<String,Object> map2 = new HashMap<>();
    map2.put("one",1);
    map2.put("two",2);
    map2.put("three",3);
    map2.put("four",4);
    map2.put("zero",null);

    // true
    System.out.println(map.equals(map2));
}
13.哈希值 hashCode()

注意:计算方法 —— 依次递增上一个值的哈希值

// 源码
public int hashCode() {
    int h = 0;
    Iterator<Entry<K,V>> i = entrySet().iterator();
    // 哈希值依次递增上一个值的哈希值
    while (i.hasNext())
        h += i.next().hashCode();
    return h;
}
14.输出为字符串 toString()

注意:输出格式 —— {key1=value1, key2=value2}

// 源码
public String toString() {
    Iterator<Entry<K,V>> i = entrySet().iterator();
    if (! i.hasNext())
        return "{}";

    StringBuilder sb = new StringBuilder();
    sb.append('{');
    for (;;) {
        Entry<K,V> e = i.next();
        K key = e.getKey();
        V value = e.getValue();
        sb.append(key   == this ? "(this Map)" : key);
        sb.append('=');
        sb.append(value == this ? "(this Map)" : value);
        if (! i.hasNext())
            return sb.append('}').toString();
        sb.append(',').append(' ');
    }
}
// 示例:HashMap实例
@Test
void contextLoads() {
    Map<String,Object> map = new HashMap<>();
    map.put("one",1);
    map.put("two",2);
    map.put("three",3);
    map.put("four",4);
    map.put("zero",null);
    map.put("six",null);
    // {zero=null, six=null, four=4, one=1, two=2, three=3}
    System.out.println(map.toString());
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/874223.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存