集合

集合

使用集合会产生的问题

对于ArrayList、HashMap等这种常用的非线程安全的集合,在高并发中使用时,会出现多线程环境下的安全问题。

一、概述

Java中的集合类主要由两个接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 集合框架的根接口。

在这里插入图片描述

三种集合的示意图:从图中可知,如果访问 List 集合,可以直接根据元素索引来访问;如果访问 Map 集合中的元素,可以根据每项元素的 key 来访问其 value ;如果访问 Set 集合中的元素,则只能根据元素本身来访问(这也是 Set 集合里元素不能重复的原因)

在这里插入图片描述

二、Collection 和 Iterator 接口

2.1 Collection 集合常用方法

JDK1.5之前,把一个对象放入集合中时,它会忘记这个对象的类型,所以会造成集合元素丢失的情况,但是在JDK1.5之后,可以使用泛型来指定限制集合中元素的类型。

  • boolean add(Object o):添加元素

  • boolean addAll(Collection c):把集合 c 中的所有元素添加到指定集合中

  • void clear():清除集合中所有元素,将集合的长度变为0

  • boolean contains(Object o):返回集合中是否包含指定元素

  • boolean containsAll(Collection c):返回集合中是否包含集合 c 中的所有元素

  • boolean isEmpty():返回集合是否为空,通过判断集合的长度来判断是否为空的

  • Iterator iterator():返回一个 Iterator 对象,用于遍历集合中的元素

  • boolean remove(Object o):删除集合中的指定元素,若存在多个重复的,则删除第一个符合条件的元素

  • boolean removeAll(Collection c):从集合中删除集合 c 中包含的所有元素

  • boolean retainAll(Collection c):从集合中删除集合 c 中不包含的所有元素

  • in size():返回集合中所有元素的个数

  • Object[] toArray():把集合转换为数组

2.2 集合的遍历

2.2.1 使用 Lambda 表达式遍历集合

JDK1.8为 Iterable 接口新增了一个 forEach(Consumer action)默认方法,该方法所需参数的类型是一个函数式接口,而Iterable 接口是 Collection 接口的父接口,因此 Collection 接口也可以直接调用该方法。

当程序调用 forEach(Consumer action)遍历集合时,会调用 Consumer 的 accept(T t) 方法,因为 Consumer 是函数式接口,所以可以使用 Lambda 表达式来遍历集合。

// Iterable中有forEach(Consumer action)方法
public interface Iterable<T> {

    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}
// Collection的父接口是Iterable,所以可以调用Iterable中的方法
public interface Collection<E> extends Iterable<E>
// 使用forEach方法遍历集合
public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("1");
        c.add("测试");
        c.add("test");
        c.forEach(obj -> {
            System.out.println("obj = " + obj);
        });
    }
}
2.2.2 使用 Iterator 遍历集合

Iterator 接口也是 Java集合框架中的成员,但是 Collection 和 Map 集合主要用于存放其他对象,而 Iterator 主要用于遍历(迭代访问) Collection 集合中的元素,Iterator 对象也被称为迭代器。

Iterator 接口中定义了四个方法:

boolean hasNext():如果被迭代的集合元素还没有被遍历完,返回 true

E next():返回集合中的下一个元素

void remove():删除集合中上一次 next 方法返回的元素

void forEachRemaining(Consumer action):是JDK1.8为 Iterator 新增的默认方法,可以使用 Lambda 来遍历集合元素

// Iterator 接口
public interface Iterator<E> {

    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

   
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
// 使用 Iterator 来遍历元素
public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("1");
        c.add("测试");
        c.add("test");
        Iterator iterator = c.iterator();
        while (iterator.hasNext()){
            // next()方法返回的Object类型,因此需要强制转换
           String c1 = (String)iterator.next();
           if(c1.equals("测试")){
               // 从集合中删除上一次 next() 方法返回的元素
                iterator.remove();
           }
           // 对c1赋值,不会改变集合元素本身
            c1 = "ccc";
        }
    }
}

当 Iterator 对集合元素进行迭代时,Iterator 不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素没有任何影响,只有通过 Iterator 的 remove() 方法删除上一次 next() 方法返回的集合元素才行。否则会引发 java.util.ConcurrentModificationException 异常

public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("1");
        c.add("测试");
        c.add("test");
        Iterator iterator = c.iterator();
        while (iterator.hasNext()){
            // next()方法返回的Object类型,因此需要强制转换
           String c1 = (String)iterator.next();
           if(c1.equals("测试")){
               // 在迭代过程中,不可修改集合元素,否则会引发异常如下代码
                c.remove(c1);
           }
        }
    }
}

Iterator 迭代器采用的是快速失败机制(fail-fast),一旦在迭代过程中检测到该集合已经被修改,程序会立即引发ConcurrentModificationException异常,而不是显示修改后的结果,避免因共享资源而引发潜在问题。

2.2.3 使用 Lambda 表达式遍历 Iterator

JDK1.8 为 Iterator 新增了一个 forEachRemaining(Consumer action)方法,与 foeEach(Consumer action) 方法相似

public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("1");
        c.add("测试");
        c.add("test");
        Iterator iterator = c.iterator();
        iterator.forEachRemaining(o -> System.out.println("o = " + o));
    }
}
2.2.4 使用 foreach 循环遍历元素

与 Iterator 相似,foreach 循环中的迭代变量也不是集合元素本身,而是元素的值,所以在循环中修改迭代遍历的值不会改变集合元素本身;同样,使用 foreach 循环遍历集合元素时,该集合也不能发生改变,否则将引发java.util.ConcurrentModificationException 异常

public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("1");
        c.add("测试");
        c.add("test");
        for (Object obj : c) {
            System.out.println("c = " + c);
        }
    }
}
2.2.5 使用 Predicate 操作集合

JDK1.8 为 Collection 集合新增了一个 removeIf(Predicate filter) 方法,该方法将会批量删除符合 filter 条件的所有元素。该方法需要一个 Predicate(谓词) 对象作为参数,Predicate 也是函数式接口,因此可以使用 Lambda 表达式作为参数。

public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("1");
        c.add("测试");
        c.add("test");
        // 删除长度小于2的所有元素
        c.removeIf(ele->((String)ele).length()<2);
        // 结果:c = [测试, test]
        System.out.println("c = " + c);
    }
}

先定义了一个 calAll() 方法,该方法使用 Predicate 判断集合中的每个元素,是否符合特定条件。

public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("java1");
        c.add("java11");
        c.add("数据结构");
        c.add("结构");
        // 统计集合c中包含字符串"java"的元素个数;结果:2
        System.out.println("包含 java 字符串的元素个数为:"+calAll(c,ele->((String)ele).contains("java")));
        // 统计集合c中包含字符串"1"的元素个数;结果:2
        System.out.println("包含 1 字符串的元素个数为:"+calAll(c,ele->((String)ele).contains("1")));
        // 统计集合c中包含字符串"数据"的元素个数;结果:1
        System.out.println("包含 数据 字符串的元素个数为:"+calAll(c,ele->((String)ele).contains("数据")));
        // 统计集合c中包含字符串"结构"的元素个数;结果:2
        System.out.println("包含 结构 字符串的元素个数为:"+calAll(c,ele->((String)ele).contains("结构")));
    }
    public static int calAll(Collection c, Predicate p){
        int total = 0;
        for(Object o : c){
            // 使用 Predicate 的 test() 方法判断该对象是否满足 Predicate 指定的条件
            if(p.test(o)){
                total++;
            }
        }
        return total;
    }
}
2.2.6 使用 Stream 操作集合

JDK1.8 还新增了 Stream 流式 API ,Stream 是一个通用的流接口,IntStream、LongStream、DoubleStream 代表元素类型为int、long、double 的流。

可以通过 Builder 来创建对应的流。

  1. 使用 Stream 或 XxxStream 的 builder() 类方法创建该 Stream 对应的 Builder;
  2. 重复调用 Builder 的 add() 方法向该流中添加多个元素;
  3. 调用 Builder 的 build() 方法获取对应的流;
  4. 调用 Stream 的聚集方法。
public class CollectionEach {
    public static void main(String[] args) {
        IntStream builder = IntStream.builder()
                .add(1)
                .add(2)
                .add(2)
                .add(3)
                .add(5)
                .build();
        // 聚集方法的代码每次都只能执行一次
        System.out.println("builder 元素中的最大值" + builder.max().getAsInt());
        //System.out.println("builder 元素中的最小值" + builder.min().getAsInt());
        //System.out.println("builder 所有元素的总和" + builder.sum();
        //System.out.println("builder 所有元素的总数" + builder.count();
        //System.out.println("builder 所有元素的平均值" + builder.average();
        //System.out.println("builder 所有元素的平方是否都大于9" + builder.allMatch(ele -> (ele << 2) > 9));
        // 将 bulider 映射为一个新的 Stream 并将所有的元素是原 Stream 元素的 2倍+1
        IntStream news = builder.map(ele -> ele * 2 + 1);
    }
}
  • Stream 提供了大量的方法进行聚集操作,这些方法既可以是中间的(intermediate),也可以是末端的(terminal):
  • 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法,比如 map() 方法就是中间方法,中间方法返回的是另一个流。
  • 末端方法:末端方法是对流的最终操作。当某个 Stream 执行末端方法后,该流将会被消耗,且不可再用。例如sum()、count()方法
  • 有状态的方法:这些方法会给流增加一些新的属性,比如元素的唯一性、元素的最大数量等等。有状态的方法往往需要更大的性能开销。
  • 短路方法:短路方法可以尽早结束对流的操作,不必检查所有的元素。

Stream 常用的中间方法:

  • filter(Predicate predicate):过滤 Stream 中不符合 predicate 条件的元素
  • mapToXxx(ToXxxFunction mapper):使用 ToXxxFunction 对流中的元素执行一对一的转换,该方法返回的新流中包含了 ToXxxFunction 转换生成的所有元素
  • peek(Consumer action):依次对每个元素执行一些操作,该方法返回的流与原有流包含相同的元素
  • distinct:用于排序流中所有重复的元素,这是一个有状态的方法
  • sorted():该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法
  • limit(long maxSize):该方法用于保证对该流的后续访问中最大允许访问的元素个数。这是一个有状态的、短路方法

Stream 常用的末端方法:

  • forEach(Consumer action):遍历流中所有元素
  • toArray():将流中所有元素转换为一个数组
  • reduce():有三个重载版本,都用于通过某种操作来合并流中的元素
  • min():返回流中所有元素的最小值
  • max():返回流中所有元素的最大值
  • count():返回流中所有元素的个数
  • anyMatch(Predicate predicate):判断流中是否至少有一个元素符合 predicate 条件
  • allMatch(Predicate predicate):判断流中是否每一个元素都符合 predicate 条件
  • noneMatch(Predicate predicate):判断流中是所有元素都不符合 predicate 条件
  • findFirst():返回流中的第一个元素
  • findAny():返回流中的任意一个元素
// 对集合操作每次都新建一个calAll过于麻烦,使用Stream自有的方法会快捷很多
public class CollectionEach {
    public static void main(String[] args) {
        Collection c = new ArrayList<>();
        c.add("java1");
        c.add("java11");
        c.add("数据结构");
        c.add("结构");
        System.out.println(c.stream().filter(ele->((String)ele).contains("java")).count());
        System.out.println(c.stream().filter(ele->((String)ele).contains("结构")).count());
        System.out.println(c.stream().filter(ele->((String)ele).length()>2).count());
        c.stream().mapToInt(ele->((String)ele).length())// 调用 mapToInt 方法获取原有Stream对应的IntStream
                            .forEach(System.out::println); // 输出5、6、4、2
    }
}

三、Set 集合

Set 集合与 Collection 集合基本相同,没有提供任何额外的方法,只是 Set 集合不允许插入重复的元素。

3.1 HashSet

HashSet 按 Hash 算法来存储集合的,因此具有很好的存取和查找性能。

HashSet 的特点:

  • 不能保证元素的排列顺序,顺序与插入顺序可能有所不同
  • HashSet 不是同步的
  • 集合的元素值可以是 Null

当向 HashSet 集合中存入一个元素时,HashSet 会调用 HashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。如果两个元素通过 equals() 方法比较返回 true ,但它们的 hashCode() 方法返回值不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。

HashSet 中每个能存储元素的“槽位”(slot)也叫“桶”(bucket),如果多个元素的 hashCode 值相同,但他们通过 equals() 方法比较返回false,就需要在一个 bucket 里放多个元素,会导致性能下降。

3.2 LinkedHashSet

HashSet 的子类 LinkedHashSet ,LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存放位置,但它同时使用链表维护元素的次序,但它依然时 HashSet 不允许集合元素重复。

3.3 TreeSet

TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。它与 HashSet 相比,多了如下方法:

  • Comparator comparator():如果 TreeSet 使用了定制排序,则该方法返回定制排序所使用的 Comparator,如果 TreeSet 采用了自然排序,则返回 Null
  • Object first():返回集合中第一个元素
  • Object last():返回集合中最后一个元素
  • Object lower(Object e):返回集合中位于指定元素之前的元素(小于指定元素的最大元素,参考的不是元素在 TreeSet 中的位置)
  • Object higher(Object e):返回集合中位于指定元素之后的元素(大于指定元素的最小元素,参考的不是元素在 TreeSet 中的位置)
  • SortedSet subSet(Object fromElement,Object toElement):返回此 Set 的子集合,范围从 fromElement(包含)—toElement(不包含)
  • SortedSet headSet(Object toElement):返回此 Set 的子集,由小于 toElement 的元素组成
  • SortedSet tailSet(Object fromElement):返回此 Set 的子集,由大于或等于 fromElement 的元素组成

TreeSet 采用红黑树的数据结构来存储集合元素,TreeSet 支持两种排序方法:自然排序和定制排序,默认情况下使用自然排序

TreeSet 会调用集合元素的 compareTo(Object obj) 方法比较元素之间的大小,然后将集合元素按升序排列,这就是自然排序。若要将一个对象添加到 TreeSet 中时,则该对象的类必须实现 Comparable 接口,否则将会抛出 ClassCastException 异常。

若要 TreeSet 正常运作,TreeSet 只能添加同一种类型的对象,且对象的类必须实现 Comparable 接口。

四、List集合

4.1 List

List 集合代表一个元素有序、可重复的集合,集合中的每一个元素都有对应的顺序索引。

List 集合常用方法:

  • void add(nt index,Object element):将元素 element 添加到 List 集合的 index 处
  • boolean addAll(int index,Collection c):将集合 c 中包含的所有元素插入到 List 集合的 index 处
  • Object get(int index):返回集合 index 索引处的元素
  • int indexOf(Object o):返回对象 o 在集合中第一次出现的位置索引
  • int lastIndexOf(Object o):返回对象 o 在集合中最后一次出现的位置索引
  • Object remove(int index):删除并返回 index 索引处的元素
  • Object set(int index,Object element):将 index 索引处的对象替换为 element,并返回被替换的旧元素
  • List subList(int fromIndex,int toIndex):返回从索引 fromIndex(包含)—toIndex(不包含) 处所有集合元素组成的子集合

jdk1.8之后添加的方法

  • void replaceAll(UnaryOperator operator):根据 operator 指定的计算规则重新设置 List 集合中的所有元素
  • void sort(Comparator c):根据 Comparator 参数对 List 集合的元素排序
public class ListEach {
    public static void main(String[] args) {
        List books = new ArrayList<>();
        books.add("测试");
        books.add("测试1253");
        books.add("测试22");
        books.add("测试354");
        // 使用目标类型为 Comparator 的 Lambda 表达式对 List 集合排序
        // 结果:[测试, 测试22, 测试354, 测试1253]
        books.sort((o1,o2)->((String)o1).length() - ((String)o2).length());
        System.out.println(books);

        // 使用目标类型为 UnaryOperator 的 Lambda 表达式来替换集合中的所有元素
        // 结果:[2, 4, 5, 6]
        books.replaceAll(ele->((String)ele).length());
        System.out.println(books);
    }
}

与 Set 不同,List 还额外提供了一个 listIterator() 方法,该方法返回一个 listIterator 对象,ListIterator 继承了 Iterator 接口。ListIterator 接口在 Iterator 接口基础上增加了如下方法:

  • boolean hasPrevious():该方法返回迭代器关联的集合是否含有上一个元素
  • Object Previous():返回该迭代器的上一个元素
  • void add(Object o):在指定位置插入一个元素

ListIterator 增加了向前迭代的功能,且还能通过 add() 方法向 List 集合中添加元素,而 Iterator 只能向后迭代而且只能删除元素

public class ListEach {
    public static void main(String[] args) {
        List books = new ArrayList<>();
        books.add("测试");
        books.add("测试1253");
        ListIterator listIterator = books.listIterator();
        while (listIterator.hasNext()){
            System.out.println(listIterator.next());
            listIterator.add("------重新插入一个元素------");
        }
        // 反向迭代
        while (listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }

    }
}

4.2 ArrayList 和 Vector

ArrayList 和 Vector 都是基于数组实现的 List 类,它俩分配了一个动态的、允许再分配的 Object[] 数组,可以使用 initialCapacity 参数来设置该数组的长度,当向它俩添加元素超过了该数组长度时,它们的 initialCapacity 会自动增加。平时无需关心它们的 initialCapacity,但如果要向其中添加大量元素时,可以使用 ensureCapacity(int initialCapacity),方法一次性的增加 initialCapacity ,这样可以减少重分配的次数,从而提高性能。

initialCapacity 默认长度为10,ArrayList 与 Vector 的最大区别就是 ArrayList 是非线程安全的,Vector 是线程安全的,但是在要保证线程安全的情况下也不介意使用 Vector,可以使用工具栏 Collections 来使 ArrayList 变成线程安全的。

4.3 LinkedList

LinkedList 也是继承自 List,但是它与 ArrayList 的实现方式完全不同,ArrayList 内部以数组的形式来保存集合中的元素,因此随机访问集合元素时有较好的性能;而 LinkedList 内部以链表的形式来保存集合中的元素,因此随机访问集合中的元素时性能较差,但在删除和插入时性能比较出色。

4.4 使用 List 集合建议

  • 如果需要遍历 List 集合元素,对于 ArrayList、Vector集合,应该使用随机访问方法(get)来遍历元素;对于 LinkedList 集合,则应该采用迭代器(Iterator)来遍历集合元素
  • 如果需要经常插入、删除操作来改变包含大量数据的 List 集合的大小,可以考虑使用 LinkedList 集合。使用 ArrayList、Vector 可能需要经常重新分配内部数组的大小,效果可能教差
  • 若有多个线程需要同时访问 List 集合中的元素,可以考虑使用 Collections 将集合包装成线程安全的集合

五、Map

Map 用于保存具有映射关系的数据,因此 Map 集合中保存着两组值,一组用于保存 Map 里的 key,另外一组用于保存 Map 里的 value。key 和 value 都可以是任意引用类型的数据,Map 里面的 key 不允许重复。如果把 Map 集合里的 key 放在一起来看,它们就组成了一个 Set 集合,但确实有一个方法 keySet(),用于返回 Map 里所有 key 组成的 Set 集合。事实上,Map 提供了一个 Entry 的内部类来封装 key-value 对,而计算 Entry 存储时,则只考虑 Entry 封装的 key。从 Java 源码来看,Java 是先实现了 Map,然后通过包装一个所有 value 都为 null 的 Map 实现了 Set 集合。

  • Map 接口中定义的常用的一些方法:
  • void clear():删除该 Map 集合中所有的 key-value 对
  • boolean containKeys(Object key):查询 Map 集合中是否包含指定的 key
  • boolean containsValue(Object value):查询 Map 集合中是否包含一个或多个 value
  • Set entrySet():返回 Map 中所有 key-value对 组成的 Set 集合,每个集合元素都是 Map.Entry(Entry 是 Map 内部类) 对象。
  • Object get(Object key):返回 key 所指定的 value ,若不存在则返回 null
  • boolean isEmpty():查询该 Map 集合是否为空
  • Set keySet():返回 Map 里所有 key 组成的 Set 集合
  • Object put(Object key,Object value):添加一个 key-value对,若 Map 中已有一个与该 key 相等的 key-value对,则会覆盖
  • void putAll(Map m):将指定的 Map 集合中的 key-value对 复制到本 Map 集合中
  • Object remove(Object key):删除指定 key 所对应的 key-value ,若不存在则返回 null
  • boolean remove(Object key,Object value):java8新增的,删除 key、value 所对应的 key-value对,
  • int size():返回 Map 中 key-value对 的个数
  • Collection values():返回该 Map 中所有 values 组成的 Collection

Map 中包括一个内部类 Entry,该类封装了一个 key-value对。Entry 包含如下三个方法:

  • Object getKey():返回 Entry 中包含的 key 值
  • Object getValue():返回 Entry 中包含的 value 值
  • Object setValue(V value):设置该 Entry 中包含的 value 值,并返回新设置的 value 值

5.1 HashMap 和 Hashtable

区别:

HashMapHashtable
非线程安全线程安全
允许使用null作为key和value不允许使用null作为key和value
继承AbstractMap实现Map接口继承Dictionary实现Map接口

HashMap 在并发执行 put 操作时会引起[死循环][1],导致 CPU 利用率接近100%。因为多线程会导致 HashMap 的 Node 链表形成环形数据结构,一旦形成环形数据结构,Node 的 next 节点永远不为空,就会在获取 Node 时产生死循环。

HashMap 和 Hashtable 及其子类,采用 hash 算法来计算 key 在 hash 桶中存放的位置,并通过 hash 算法来增加 key 集合的大小。

hash 算法可以根据 hashCode 来计算在桶中的位置,接着从桶中取出元素,此时 hash 桶是 open 状态;在发生 hash冲突时,单个桶会存在多个元素,这些元素会按链表形式存储,当链表长度大于8时,且桶的数量大于64时,会转换为红黑树。

hash 表包含如下属性:

  • 容量(capacity):hash 表中桶的数量
  • 初始化容量(initail capacity):创建 hash 表时桶的数量
  • 尺寸(size):当前 hash 表中记录的数量
  • 负载因子(load factor):负载因子等于 size/capacity
  • 负载极限:0-1的数值,当负载因子达到指定的负载极限时,hash 表会自动成倍的增加桶的数量,默认值为 0.75

六、Collections

Collections 是集合的工具类,就像 Arrays 工具类一样,封装了对集合操作的很多方法,排序、替换、取值等等,详情可以查看api

最主要的作用是它提供的多个 synchronizedXxx() 方法,将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。例如:synchronizedMap、synchronizedList、synchronizedSet、synchronizedCollection

以 synchronizedMap 为例,可以看到 synchronizedMap() 方法后返回一个 synchronizedMap 类的对象,在 synchronizedMap 类中使用了 synchronized 同步关键字来保证 Map 操作是线程安全的

// synchronizedMap方法
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
       return new SynchronizedMap<>(m);
}
// SynchronizedMap类
private static class SynchronizedMap<K,V>
       implements Map<K,V>, Serializable {
       private static final long serialVersionUID = 1978198479659022715L;

       private final Map<K,V> m;     // Backing Map
       final Object      mutex;        // Object on which to synchronize

       SynchronizedMap(Map<K,V> m) {
           this.m = Objects.requireNonNull(m);
           mutex = this;
       }

       SynchronizedMap(Map<K,V> m, Object mutex) {
           this.m = m;
           this.mutex = mutex;
       }

       public int size() {
           synchronized (mutex) {return m.size();}
       }
       public boolean isEmpty() {
           synchronized (mutex) {return m.isEmpty();}
       }
       public boolean containsKey(Object key) {
           synchronized (mutex) {return m.containsKey(key);}
       }
       public boolean containsValue(Object value) {
           synchronized (mutex) {return m.containsValue(value);}
       }
       public V get(Object key) {
           synchronized (mutex) {return m.get(key);}
       }

       public V put(K key, V value) {
           synchronized (mutex) {return m.put(key, value);}
       }
       public V remove(Object key) {
           synchronized (mutex) {return m.remove(key);}
       }
       // 省略其他方法
}

七、ConcurrentHashMap

ConcurrentHashMap 是 JUC 包中的一个类,Spring 的源码中有很多使用 ConcurrentHashMap 的地方。ConcurrentHashMap 在Java7的时候,采用的是分段锁,但是在Java8的时候,摒弃了分段锁,采用了 CAS+自旋锁的形式。

[如何在java中使用ConcurrentHashMap][1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值