轻松解读源码系列之Java集合接口&抽象类(1)—Map和Collection

大家好,我是程序员xiao熊,本篇内容将为大家分享Java集合的主要接口和抽象类;这些接口和抽象类是Java集合框架的基础,为具体的子类实现提供了规范以及基础实现,降低了子类实现的成本;

Java集合的底层接口主要分为两个:Map和Collection;其对应的继承结构图如下所示;本篇内容主要是对Map、Collection、Iterable这三个接口以及其中的部分默认方法进行讲解,其他的接口和抽象类将在后续的文章中进行讲解,会按照Map和Collection体系分别讲解,并且尽量按照图中的层次从上至下,一层层讲解;

Collection体系继承结构图

Map体系继承结构图

1、Iterable接口

Iterable接口相对简单,实现这个接口的对象,可以使用for-each循环语句,也可以通过iterator遍历元素

1.1 方法说明

default void forEach(Consumer action)

对Iterable对象的每个元素执行指定的操作(action),直到所有元素都处理完毕或操作(action)抛出异常(异常抛给调用者)

Iterator iterator()

返回类型为T的元素的迭代器, T是集合元素的类型

default Spliterator spliterator()

返回Spliterator对象

2、Collection接口

集合是一组对象,对象就是集合里面的元素;在JDK中,Collection是集合对应的实现,是集合框架结构中的根接口,它提供了集合应该具备的操作规范(方法);在集合的具体实现类中,有些集合允许重复元素,有些则不允许,有些是有序的,有些是无序的,具体取决于实现类。JDK中的具体实现类并没有直接实现这个接口,因为JDK根据场景提供了更具体的子接口,如Set和List。实现类都是实现这些具体的接口或者对应的抽象方法。Collection这个接口通常用于传递集合,并在需要最大通用性的情况下对集合进行操作。

集合规范中提到,Collection的实现类一般可以提供两个构造函数

1. 无参构造函数:创建一个空的集合

2. 入参类型是Collection的构造函数:目的是创建一个包含入参集合元素的集合(即复制入参集合)

Collection的具体实现类根据场景,在实现Collection接口的方法时,会抛出一些异常,具体如下:

1. 如果collection的实现类不支持的操作,则实现类在方法体中需要抛出
UnsupportedOperationException异常

2. Collection的不同实现类对元素类型可能会有限制,例如不能null,或者只接受指定的元素类型;因此可能会抛出非检查异常,例如:NullPointerException或ClassCastException;添加不符合的元素时,可能抛出异常,可能执行成功(只是方法执行成功),但是元素并不会进入到集合中;对于查询不符合条件的元素,会直接返回false

3. Collection默认不支持同步控制,实现类可以自行指定具体使用的同步控制策略;

4. 默认方法实现(继承的或其他方式)没有使用任何同步协议。如果集合的实现类需要使用同步协议,那么它必须覆盖默认方法的实现以使用同步协议。

使用的注意事项:

集合的元素需要注意,如果元素直接或者间接包含了自身引用,则某些对集合执行递归遍历的操作可能会失败(抛出异常),例如clone()、equals()、hashCode()和toString()方法。具体示例如下:

// 集合元素
public class SelfRef {
    SelfRef selfRef;
    public SelfRef getSelfRef() {
        return selfRef;
    }
    public void setSelfRef(SelfRef selfRef) {
        this.selfRef = selfRef;
    }
    @Override
    public String toString() {
        return selfRef.toString();
    }
}

// 集合操作示例代码
ArrayList selfRefList = new ArrayList<>();
SelfRef selfRef = new SelfRef();
selfRef.setSelfRef(selfRef);
selfRefList.add(selfRef);
selfRef.toString(); // 抛出异常

2.1、方法说明

boolean add(E e)


添加元素到集合中

boolean addAll(Collection c)


添加一个集合中的元素到当前集合中

void clear()


清除集合中的元素

boolean contains(Object o)


判断集合是否包含指定的对象

boolean containsAll(Collection c)


判断是否包含指定集合中所有的元素

boolean equals(Object o)


判断是否与指定的对象相等

int hashCode()


返回集合的hash码

boolean isEmpty()


判断集合是否为空

Iterator iterator()


返回包含元素的iterator对象

default Stream parallelStream()


返回并发执行的流对象(这是默认方法,已有具体逻辑)

boolean remove(Object o)


删除指定的元素

boolean removeAll(Collection c)


删除存在于指定集合中所有的元素

default boolean removeIf(Predicate filter)


根据谓词对象删除符合条件的元素(这是默认方法,已有逻辑)

boolean retainAll(Collection c)


保留存在于指定集合中的所有元素

int size()


返回返回集合中元素的数量

default Spliterator spliterator()


返回包含元素的spliterator对象(这是默认方法,已有逻辑)

default Stream stream()


返回包含元素的流对象(这是默认方法,已有逻辑)

Object[] toArray()


将元素以Object数组形式返回,返回的数组与集合互不影响

T[] toArray(T[] a)

返回指定类型的数组,返回的数组与集合互不影响

2.2、方法解析

对于Collection接口,本小节对removeIf方法进行分析说明,该方法是删除集合中符合谓词表达式的元素;其业务逻辑过程如下

1. 谓词对象非空校验

2. 获取迭代器iterator

3. 利用while循环和iterator遍历链表的中的元素

4. 利用谓词对象的test方法验证元素是否符合谓词表达式,符合,则利用iterator删除该元素,然后返回步骤3,进行下一轮循环;

removeIf方法具体代码如下:

default boolean removeIf(Predicate filter) {
    // 1.	谓词对象非空校验
Objects.requireNonNull(filter);
    boolean removed = false;
    // 2.	获取迭代器iterato
final Iterator each = iterator();
    // 3.	利用while循环和iterator遍历链表的中的元素
while (each.hasNext()) {
// 4.利用谓词对象的test方法验证元素是否符合谓词表达式,符合,则利用iterator删除该元素,
  // 然后返回步骤3,进行下一轮循环
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}

3、Map

Map是将key和value进行映射的对象;一个Map对象不能包含重复的key值,一个key最多只能映射到一个value,即key 与value是一一对应的;Map接口的出现是对抽象类Dictionary的替代;

Map接口提供了三个视图:key的集合视图,value的集合视图,key-value映射关系的集合视图;Map的顺序是指Map集合视图中迭代器返回元素的顺序。有些Map接口的实现类会明确指定元素的顺序,比如TreeMap类;其他实现类则不一定能够保证元素的顺序是固定的,如HashMap类。

集合规范中提到,Map实现类一般可以提供两个构造函数:

1. 无参构造函数,用于创建一个空的Map;

2. 只有一个Map类型的参数的构造函数:用于创建一个新的Map,其key-value的映射关系与参数Mapd对象相同。实际上是允许用户复制任何Map,生成一个等效的Map。

Map的实现类根据不同的场景使用不同的实现策略,总体原则如下:

1. 如果不支持修改Map的方法,则在操作修改Map的方法时,会抛出UnsupportedOperationException异常。

2. 有些Map的实现对包含的key和value有限制。例如,有些实现类禁止空的key和空value,有些实现类对key的类型有限制。尝试插入不符合条件的key或value会抛出未检异常,通常是NullPointerException或ClassCastException。查询不符合条件的key或value可能会抛出异常,也可能直接返回false。更一般地说,如果对不符合条件的key或value进行操作,而操作可能会抛出异常,也可能会成功,这取决于实现方式; 但操作无论成功还是失败都不会导致不符合条件的元素插入到Map中,则这种异常在此接口的规范中标记为“可选”。

使用中的注意事项:

1. 如果使用可变对象作为Map的键,则必须要注意。因为如果一个对象的值发生了改变(这个会影响equals方法的比较结果),而该对象是Map中的key,那么Map的行为将会变得不确定。还有一个特殊情况是,不允许Map将自身作为key。虽然Map允许将自身包含为一个value,但我们要特别小心:equals和hashCode方法的定义已经不适用这样的Map了。

2. 一些对Map进行递归遍历的操作可能会失败,当map直接或间接包含自己时,会出现自引用实例的异常。其中包括clone()、equals()、hashCode()和toString()方法。具体如下:

// 元素
public class SelfRef {
    SelfRef selfRef;
    public SelfRef getSelfRef() {
        return selfRef;
    }
    public void setSelfRef(SelfRef selfRef) {
        this.selfRef = selfRef;
    }
    @Override
    public String toString() {
        return selfRef.toString();
    }
}

// 示例代码
Map selfRefMap = new HashMap<>();
selfRefMap.put("selfRef", selfRef);
selfRefMap.toString();// 抛出异常

3.1、方法说明

void clear()


清除Map中的元素

default V compute(K key, BiFunction remappingFunction)


尝试为指定的key及其当前的value(如果key不在Map中则value为空)计算新的value值;如果key在Map中,并且新的value不为空,则更新value值,否则删除key对应元素

如果Key不在Map中,新的value不为空,则将key和新的value加入到Map中,如果新的value为空,则Map保持不变

default V computeIfAbsent(K key, Function mappingFunction)


为指定的key计算新的value值;如果key不在Map中,或则映射到null,并且新的value不为null,则加入Map;否则Map保持不变

default V computeIfPresent(K key, BiFunction remappingFunction)


为指定的key计算新的value值;如果key在Map中,并且对应的value不为空;在新的value不为null时,则Map中key对应的value;否则删除Map中的key

boolean containsKey(Object key)


判断是否包含指定的key

boolean containsValue(Object value)


判断是否包含指定的value

Set<Map.Entry> entrySet()


返回key-value对象的集合视图

boolean equals(Object o)


判断当前Map是否等于指定的对象

default void forEach(BiConsumer action)


循环对元素执行指定的操作action

V get(Object key)


获取指定key对应的value,如果key不存在,则返回null

default V getOrDefault(Object key, V defaultValue)


获取指定key对应的value,如果key不存在,则返回指定的默认值defaultValue

int hashCode()


返回Map的hashcode

boolean isEmpty()


判断Map是否为空,但map为null,则会抛出异常

Set keySet()

返回Key对象的集合视图

default V merge(K key, V value, BiFunction remappingFunction)

计算key对应的value值,如果原的value为null或者key不存在,则将参数的value作为key的新value;如果key对应的原value不为空,则通过remappingFunction重新计算新的value值,如果新的value值不为空,则更新key的value值或者将key添加至集合中,如果新的value为空,则删除key对应的元素

V put(K key, V value)

将Key与value进行关联

Void putAll(Map m)

将入参Map中key-value对象复制到当前的Map中

default V putIfAbsent(K key, V value)

为当前存在于Map中的key关联新的value值

V remove(Object key)

删除指定的key对应的元素

default Boolean remove(Object key, Object value)

删除Map中key和value与指定的key和value匹配的元素

default V replace(K key, V value)

替换key对应的value,如果key不存在,则不操作

default Boolean replace(K key, V oldValue, V newValue)

如果key的原value与入参oldvalue匹配,则替换key的value为newValue,如果不满足,则不操作

default void replaceAll(BiFunction function)

将Map中所有的key的value进行更新,新的value由入参指定的函数确定

Int size()

返回Map中元素刷领

Collection values()

返回value的集合视图

3.2、方法解析

jdk1.8开始,接口允许默认方法有方法,本小节针对以下几个默认方法进行讲解分析:

1. compute(…)

2. computeIfAbsent(…)

3. computeIfPresent(…)

4. merge(…)

3.2.1、Compute方法

Compute方法是根据当前的key以及对应value,利用remappingFunction对象计算新的value值,具体业务逻辑如下:

1. remappingFunction对象的非空校验,remappingFunction对象是用于计算新的value的值

2. 根据key获取对应的value

3. 根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值

4. 新的value值(newValue)等于null,

4.1. 如果key存在于Map中,则需要删除key对应的元素,返回null;

4.2. 如果key不在Map中,则不处理,Map保持不变,返回null;

5. 新的value值不等于null,则将key和新的value值存入Map中,返回新的value值

代码如下:

default V compute(K key,
        BiFunction remappingFunction) {
    // 1.	remappingFunction对象的非空校验,remappingFunction对象是用于计算新的value的值
Objects.requireNonNull(remappingFunction);
    // 2.	根据key获取对应的value
V oldValue = get(key);
// 3.	根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值
    V newValue = remappingFunction.apply(key, oldValue);
    // 4. 新的value值(newValue)等于null
if (newValue == null) {

// 4.1如果key存在于Map中,则需要删除key对应的元素,返回null;
        if (oldValue != null || containsKey(key)) {
            // something to remove
            remove(key);
            return null;
        } else {
            // 4.2	如果key不在Map中,则不处理,Map保持不变,返回null.
            return null;
        }
    } else {
        // 5.新的value值不等于null,则将key和新的value值存入Map中,返回新的value值
// add or replace old mapping
        put(key, newValue);
        return newValue;
    }
}

3.2.2、computeIfAbsent

computeIfAbsent与Compute类似,在新的value值不为null的前提下,只有在key不存在Map中,或者key对应的原value为null时,才会更新key的value值为新的值;具体业务逻辑如下:

1. mappingFunction对象的非空校验,mappingFunction对象是用于计算新的value的值

2. 根据key获取对应的value

3. 如果value为null(表示key不存在,或者对应的value为null),则根据key,通过mappingFunction的apply方法计算新的value值

4. 新的value值(newValue)不等于null, 则将key和新的value值存入Map中,返回新的value值

代码如下:

default V computeIfAbsent(K key,
        Function mappingFunction) {
    // 1.mappingFunction对象的非空校验
Objects.requireNonNull(mappingFunction);
    V v;
    // 2.	根据key获取对应的value
if ((v = get(key)) == null) {
        V newValue;
        // 3.如果value为null(表示key不存在,或者对应的value为null),
       //则根据key,通过mappingFunction的apply方法计算新的value值
        if ((newValue = mappingFunction.apply(key)) != null) {
            // 4.	新的value值(newValue)不等于null, 
          // 则将key和新的value值存入Map中,返回新的value值
            put(key, newValue);
            return newValue;
        }
    }

    return v;
}

3.2.3、computeIfPresent

computeIfPresent与Compute类似,在key存在Map中的前提下,新的value不为null时,才会更新key的value值为新的值,新的value为null时,删除key对应的元素;具体业务逻辑如下:

1. remappingFunction对象的非空校验,remappingFunction对象是用于计算新的value的值

2. 根据key获取对应的value

3. 如果原有的value不为null,才处理新的值

3.1. 根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值

3.2. 新的value值不等于null,则将key和新的value值存入Map中,返回新的value值

3.3. 新的value值(newValue)等于null,则需要删除key对应的元素,返回null;

4. 如果原有的value为null,或者key不存在,则不做任何操作,直接返回null

default V computeIfPresent(K key,
        BiFunction remappingFunction) {
    // 1	remappingFunction对象的非空校验
Objects.requireNonNull(remappingFunction);
    V oldValue;
    // 2	根据key获取对应的value
// 3	如果原有的value不为null,才处理新的值
if ((oldValue = get(key)) != null) {
        // 3.1	根据key、原有的value(oldValue),通过remappingFunction的apply方法计算新的value值
V newValue = remappingFunction.apply(key, oldValue);
        // 3.2新的value值不等于null,则将key和新的value值存入Map中,返回新的value值
if (newValue != null) {
            put(key, newValue);
            return newValue;
        } else {
// 3.3	新的value值(newValue)等于null,则需要删除key对应的元素,返回null;
            remove(key);
            return null;
        }
    } else {
// 4	如果原有的value为null,或者key不存在,则不做任何操作,直接返回null
        return null;
    }
}

3.2.4、merge

merge方法生成value的方式比之前的所有类似;计算新的value由两种方式

  • 将入参的第二个参数作为新的value值
  • 根据第二个参数value和原value,利用remappingFunction对象计算新的value的值

具体业务逻辑过程如下:

1. remappingFunction对象的非空校验

2. 方法的value对象(第二个参数)的非空校验

3. 根据key获取对应的原value

4. 如果原value为null,则取入参的第二个参数value作为新的值;如果原有的value不为null,则使用原有的value、入参的value(第二个参数),通过remappingFunction计算新的value

5. 如果新的value为null,将key从Map中删除,返回新的value值(为null)

6. 如果新的value不为null,将key和新的value值存入Map中,返回新的value值

具体代码如下:

default V merge(K key, V value,
        BiFunction remappingFunction) {
    // 1	remappingFunction对象的非空校验
    Objects.requireNonNull(remappingFunction);
     // 2	方法的value对象(第二个参数)的非空校验
    Objects.requireNonNull(value);

   // 3	根据key获取对应的原value
    V oldValue = get(key);
   // 4	如果原value为null, 则取入参的第二个参数value作为新的值;如果原有的value不为null,则使用原有的value、入参的value(第二个参数),通过remappingFunction计算新的value
    V newValue = (oldValue == null) ? value :
               remappingFunction.apply(oldValue, value);
    //5. 如果新的value为null,将key从Map中删除,返回新的新的value值
if(newValue == null) {
        remove(key);
    } else {
        // 6. 如果新的value不为null,将key和新的value值存入Map中,返回新的value值
        put(key, newValue);
    }
    return newValue;
}

欢迎大家关注【程序员xiao熊】,今天的分享就到这里,欢迎大家在评论区进行交流

原文链接:,转发请注明来源!