`

JDK1.5之中的Map相关的类

 
阅读更多

java5的java.util包提供了大量集合类。其中最常用的集合类有List、Set、Map等。这篇文章主要介绍其中的Map。

首先,来看下java.util包中Map相关的集合类的类图。

 

 

接口Map是整个类图的跟,Map往下又提供了两个接口:ConcurrentMap和SortedMap。ConcurrentMap是java5中新增的线程安全的Map接口;而SortedMap则是支持排序的Map接口。在下面这些具体的实现类中,常用的就属Hashtable、HashMap和TreeMap了。另外,java5新增了HashMap的并发版本ConcurrentHashMap。下面主要介绍下这几个类。

 

在开始之前,先介绍下Map是什么?

javadoc中对Map的解释如下:

An object that maps keys to values . A map cannot contain duplicate keys; each key can map to at most one value.

This interface takes the place of the Dictionary class, which was a totally abstract class rather than an interface.

The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings.

 从上可知,Map用于存储“key-value”元素对,它将一个key映射到一个而且只能是唯一的一个value。

Map可以使用多种实现方式,HashMap的实现采用的是hash表;而TreeMap采用的是红黑树。

 

1. Hashtable 和 HashMap

这两个类主要有以下几方面的不同:

    Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类。

 

    在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。 当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null

 

   这两个类最大的不同在于Hashtable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。而HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。因此,在Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:

 

Java代码 复制代码 收藏代码
  1. public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {   
  2.     return new SynchronizedMap<K,V>(m);   
  3.  }  
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
	return new SynchronizedMap<K,V>(m);
 }

该方法返回的是一个SynchronizedMap 的实例。SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized 关键字进行了同步控制。

 

2. 潜在的线程安全问题

上面提到Collections为HashMap提供了一个并发版本SynchronizedMap。这个版本中的方法都进行了同步,但是这并不等于这个类就一定是线程安全的。在某些时候会出现一些意想不到的结果。

如下面这段代码:

Java代码 复制代码 收藏代码
  1. // shm是SynchronizedMap的一个实例   
  2. if(shm.containsKey('key')){   
  3.         shm.remove(key);   
  4. }  
// shm是SynchronizedMap的一个实例
if(shm.containsKey('key')){
        shm.remove(key);
}

 这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey和reomve方法都是同步的,但是整段代码却不是。考虑这么一个使用场景:线程A执行了containsKey方法返回true,准备执行remove操作;这时另一个线程B开始执行,同样执行了containsKey方法返回true,并接着执行了remove操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了。要保证这段代码按我们的意愿工作,一个办法就是对这段代码进行同步控制,但是这么做付出的代价太大。

 

在进行迭代时这个问题更改明显。Map集合共提供了三种方式来分别返回键、值、键值对的集合:

Java代码 复制代码 收藏代码
  1. Set<K> keySet();   
  2.   
  3. Collection<V> values();   
  4.   
  5. Set<Map.Entry<K,V>> entrySet();  
Set<K> keySet();

Collection<V> values();

Set<Map.Entry<K,V>> entrySet();

 在这三个方法的基础上,我们一般通过如下方式访问Map的元素:

Java代码 复制代码 收藏代码
  1. Iterator keys = map.keySet().iterator();   
  2.   
  3. while(keys.hasNext()){   
  4.         map.get(keys.next());   
  5. }  
Iterator keys = map.keySet().iterator();

while(keys.hasNext()){
        map.get(keys.next());
}

 

在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本” 。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出 ConcurrentModificationException异常。为了解决这个问题通常有两种方法,一是直接返回元素的副本,而不是视图。这个可以通过

集合类的 toArray() 方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。


3. 更好的选择:ConcurrentHashMap

java5中新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMap。ConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

 

上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。

 

在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。

 

  • 大小: 32.4 KB
分享到:
评论

相关推荐

    jdk1.5API中文chm版

    1. **泛型(Generics)**:JDK 1.5首次引入泛型,允许在类、接口和方法中使用类型参数,提高了代码的类型安全性和重用性。泛型使得集合类(如List、Set、Map)可以明确地指定元素类型,避免了类型转换异常。 2. **...

    JDK1.5中文版

    1. **泛型(Generics)**:泛型是JDK1.5引入的一项重要功能,它允许在类、接口和方法中定义类型参数,以增强类型安全性,减少运行时的类型转换,并且在编译阶段就能捕获可能存在的类型错误。泛型的使用使得集合框架...

    JDK1.5新特性

    2. **泛型**:泛型是1.5最重要的新特性之一,它允许在类、接口和方法中使用类型参数,增强了类型安全性和代码重用性。泛型可以限制集合元素的类型,避免了类型转换错误。 3. **枚举类(enum)**:之前的Java中,...

    详细描述jdk1.5新特性

    ### 详细描述 JDK 1.5 新特性:泛型 #### 一、引言 Java 5.0(也称为 JDK 1.5)引入了一系列重要的新特性,这些特性极大地提高了开发者的编程效率和代码质量。其中最显著的特性之一便是泛型(Generics)。本文将...

    JDK1.5 for Linux

    在压缩包`linux_jdk1.5`中,可能包含的是已解压的JDK文件夹,包括了JRE(Java Runtime Environment)、开发工具(如javac编译器)和其他必要的库文件。开发者可以通过这个解压后的文件夹直接进行Java开发和运行。 ...

    官方JDK1.5版本

    1. 泛型(Generics):这是JDK 1.5最显著的变化之一,它允许开发者在定义类、接口和集合时指定类型参数,增强了代码的类型安全性,减少了运行时类型异常的可能性。 2. 自动装箱与拆箱:自动装箱使得基本数据类型和...

    JDK 1.5的泛型實現(Generics in JDK 1.5)

    今天,JDK1.5終於內建泛型特性,不僅編譯器不再需要 任何外力(外掛附件)的幫助,整個 Java標準程式庫也被翻新(retrofit),許多 角落針對泛型做了改寫。 讓我們把帶有「參數化型別」(parameterized types)的 ...

    JDK1.5-1.6-1.7之新特性总结-1

    泛型是JDK 1.5最重要的特性之一,它允许在类、接口和方法中定义类型参数,从而增强了类型安全性和代码重用。例如,可以定义一个通用的集合类`List&lt;T&gt;`,其中T代表任意类型。泛型还可以用于限制方法的参数类型,避免...

    java JDK1.5---32位和64位

    1. **泛型(Generics)**:这是JDK 1.5最重要的特性之一,允许在类、接口和方法中声明参数化类型,从而提高了代码的类型安全性和重用性。泛型有助于减少类型转换的错误,并在编译时期就能发现潜在的问题。 2. **...

    jdk1.5 jdk

    1. **泛型(Generics)**:这是JDK 1.5中的核心特性之一,它允许在类、接口和方法中使用类型参数,提高了代码的类型安全性和重用性。泛型使得集合框架能够处理特定类型的元素,减少了强制类型转换的需要,并且在编译...

    JDK 1.5之Generics

    **Java泛型(Generics)**是JDK 1.5引入的一项重要特性,它极大地提高了代码的类型安全性和可读性。泛型主要是为了更好地处理集合类(如List、Set、Map等),避免了类型转换的麻烦和潜在的ClassCastException风险。 ...

    jdk1.8中文API及jQuery1.5中文API

    1. **Lambda表达式**:这是JDK 1.8最重要的特性之一,它提供了一种简洁的方式来表示匿名函数,简化了处理函数接口和集合的操作。 2. **Stream API**:Stream API允许对集合数据进行声明性处理,提供了大量操作如...

    Jdk api 1.6中文文档

    2. **集合框架**:在1.6版本中,Java的集合框架已经相当成熟,包括List(如ArrayList和LinkedList)、Set(如HashSet和TreeSet)、Map(如HashMap和TreeMap)等接口及其实现,以及实用工具类`Collections`和`Arrays`...

    jdk1.8.0-161

    在本文中,我们将详细探讨JDK 1.8.0_161版本中的关键特性和更新。 ### 1. Java 8新特性 JDK 1.8.0是Java 8的主要版本,引入了许多创新特性,旨在提高代码的可读性、简洁性和性能。以下是一些主要的新特性: #### ...

    jdk api 1.8中文版(谷歌翻译)

    泛型是JDK 1.5引入的重要特性,允许在类、接口和方法中使用类型参数。在JDK 1.8中,泛型的应用更加广泛,例如在集合框架中,泛型可以确保插入和检索的数据类型安全,避免了不必要的类型转换。 ### 2. Lambda表达式 ...

    java中map集合的用法

    ### Java中Map集合的用法详解 #### 一、引言 在Java编程语言中,`java.util`包内提供了多种强大的数据结构来帮助开发者高效地处理数据。其中,`Map`作为Java中最常用的数据结构之一,被广泛应用于各种场景。`Map`是...

    JDK1.8 64位下载

    3. 设置环境变量,包括`JAVA_HOME`指向JDK安装目录,`PATH`中添加`%JAVA_HOME%\bin`,确保系统能够找到Java命令。 4. 验证安装,通过命令行输入`java -version`,显示正确版本信息即安装成功。 ### 4. **JDK 1.8的...

    java遍历特例

    文档进一步展示了JDK 1.5中引入的新特性——增强的for循环(也称为“foreach”循环),用于遍历`Map`: ```java Map m = new HashMap(); for (Object o : m.keySet()) { m.get(o); } ``` 这种方法通过`keySet()`...

    涵盖了90%以上的面试题

    7. **JDK 版本特性**: JDK 1.5 引入了枚举、匿名内部类、自动装箱/拆箱等特性;JDK 1.7 引入了钻石操作符、try-with-resources 等;JDK 1.8 包括 Lambda 表达式、接口的默认方法和静态方法、Stream API 等。 8. **...

Global site tag (gtag.js) - Google Analytics