`
410063005
  • 浏览: 180013 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Java学习之HashMap: 如何正确实现Map的entrySet()方法

    博客分类:
  • java
 
阅读更多

通过继承AbstractMap我们可以很容易实现自己的Map,我们只需要实现唯一的抽象的entrySet()方法。 以下是来自《Jav编程思想》(第四版第17章的例子),继承AbstractMap实现了自己的SlowMap。 另外还应该注意, 如果要创建自己的Map,还必须同时定义Map.Entry的实现。

 

总结起来实现自定义Map需要以下两个步骤:

  1. 继承AbstractMap需要实现entrySet()方法
  2. 实现自己的Map.Entry

 

//: containers/MapEntry.java
// A simple Map.Entry for sample Map implementations.
import java.util.*;

public class MapEntry<K,V> implements Map.Entry<K,V> {
  private K key;
  private V value;
  public MapEntry(K key, V value) {
    this.key = key;
    this.value = value;
  }
  public K getKey() { return key; }
  public V getValue() { return value; }
  public V setValue(V v) {
    V result = value;
    value = v;
    return result;
  }
  public int hashCode() {
    return (key==null ? 0 : key.hashCode()) ^
      (value==null ? 0 : value.hashCode());
  }
  public boolean equals(Object o) {
    if(!(o instanceof MapEntry)) return false;
    MapEntry me = (MapEntry)o;
    return
      (key == null ?
       me.getKey() == null : key.equals(me.getKey())) &&
      (value == null ?
       me.getValue()== null : value.equals(me.getValue()));
  }
  public String toString() { return key + "=" + value; }
} ///:~



//: containers/SlowMap.java
// A Map implemented with ArrayLists.
import java.util.*;
import net.mindview.util.*;

public class SlowMap<K,V> extends AbstractMap<K,V> {
  private List<K> keys = new ArrayList<K>();
  private List<V> values = new ArrayList<V>();
  public V put(K key, V value) {
    V oldValue = get(key); // The old value or null
    if(!keys.contains(key)) {
      keys.add(key);
      values.add(value);
    } else
      values.set(keys.indexOf(key), value);
    return oldValue;
  }
  public V get(Object key) { // key is type Object, not K
    if(!keys.contains(key))
      return null;
    return values.get(keys.indexOf(key));
  }
  public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> set= new HashSet<Map.Entry<K,V>>();
    Iterator<K> ki = keys.iterator();
    Iterator<V> vi = values.iterator();
    while(ki.hasNext())
      set.add(new MapEntry<K,V>(ki.next(), vi.next()));
    return set;
  }
  public static void main(String[] args) {
    SlowMap<String,String> m= new SlowMap<String,String>();
    m.putAll(Countries.capitals(15));
    System.out.println(m);
    System.out.println(m.get("BULGARIA"));
    System.out.println(m.entrySet());
  }
} /* Output:
{CAMEROON=Yaounde, CHAD=N'djamena, CONGO=Brazzaville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, BOTSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, DJIBOUTI=Dijibouti}
Sofia
[CAMEROON=Yaounde, CHAD=N'djamena, CONGO=Brazzaville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, BOTSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, DJIBOUTI=Dijibouti]
*///:~

 以上是一个参考实现。这个解决方案看起来很简单并且没什么问题,但这并不是一个恰当的实现。主要问题是它创建了键和值的副本。entrySet()的恰当实现 应该在Map中提供视图,而不是副本,并且这个视图允许对原始的映射表进行修改,而副本则不行。

 

可以参考源码来学习正确的entrySet()应该如何实现。首先先看Map中entrySet()的声明

public interface Map<K,V> {
    /**
     * Returns a {@link Set} view of the mappings contained in this map.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  If the map is modified
     * while an iteration over the set is in progress (except through
     * the iterator's own <tt>remove</tt> operation, or through the
     * <tt>setValue</tt> operation on a map entry returned by the
     * iterator) the results of the iteration are undefined.  The set
     * supports element removal, which removes the corresponding
     * mapping from the map, via the <tt>Iterator.remove</tt>,
     * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
     * <tt>clear</tt> operations.  It does not support the
     * <tt>add</tt> or <tt>addAll</tt> operations.
     *
     * @return a set view of the mappings contained in this map
     */
    Set<Map.Entry<K, V>> entrySet();
}
注释文档明确指出,entrySet()返回的Set应该是Map所代表的映射表的一个View。对Map的修改应该会反映到这个Set,反过来对这个Set的修改也会反映到Map。特别地,当在这个Set上进行迭代的过程中,如果修改了Map(除非是通过这个Set的迭代器进行remov()e或setValue()操作),那么迭代过程产生的结果是不确定的。 

 

再看HashMap中entrySet()方法的实现

    public Set<Map.Entry<K,V>> entrySet() {
	return entrySet0();
    }

    private Set<Map.Entry<K,V>> entrySet0() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }

    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return newEntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K,V> e = (Map.Entry<K,V>) o;
            Entry<K,V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }
        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }
        public int size() {
            return size;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

 EntrySet是HashMap的一个内部类,它继承自AbstractSet,必须需要实现iterator()和size()两个抽象方法。而HashMap的entrySet()不过是返回了EntrySet的一个实例。

注意,与我们之前定义的SlowMap的entrySet()相比,这里的entrySet()实现中没有进行任何创建副本的操作。不难发现,remove()和clear()等方法,最终会调用到EntrySet所在的外部对象(即一个HashMap实例)的相关方法,从而实现对Map的修改应该会反映到这个Set,反过来对这个Set的修改也会反映到Map。当然,除了直接在entrySet()方法返回的Set对象上直接进行操作,还可以获取这个Set的迭代器,通过迭代器来修改Map和对应的Set。这类修改,同样也应当需要实现对Map的修改应该会反映到这个Set,反过来对这个Set的修改也会反映到Map。这里涉及到HashMap.EntryIterator类,内容较多,后面接着分析

 

分享到:
评论
2 楼 vincente 2014-03-23  
很专业
1 楼 kolen001 2013-09-23  
学习。。。。

相关推荐

    java循环Map java迭代Map

    Map a = new HashMap(); //方法一 Iterator it = a.entrySet().iterator(); while (it.hasNext()) { Map.Entry pairs = (Map.Entry) it.next(); System.out.println(pairs.getValue()); } //以下方法需要jdk5以上...

    java中Map集合的常用遍历方法及HashMap的应用实例

    1、遍历Map.entrySet():它的每一个元素都是Map.Entry对象,这个对象中, 放着的就是Map中的某一对key-value; 2、遍历Map.keySet():它是Map中key值的集合,我们可以通过遍历这个集合来 读取Map中的元素; 3、...

    Java很好的学习笔记集合Map,学习代码

    总结,本篇Java学习笔记主要围绕Map接口展开,深入讲解了Map的基本概念、方法、实现类以及遍历方式。同时,也提到了Java Web开发中的相关知识,如Cookie、Session、localStorage和sessionStorage的使用,以及Java 8...

    java中map集合的用法.doc

    创建一个Map实例通常是通过实现Map接口的具体类,如HashMap。例如: ```java Map, String&gt; map = new HashMap(); ``` 2. **插入键值对**: 使用`put()`方法将键值对放入Map中,如: ```java map.put("sa", ...

    Java使用entrySet方法获取Map集合中的元素

    在实际开发中,我们经常需要从 Map 集合中获取所有的键值对关系,而 Java 提供了多种方法来实现这一点,其中之一便是使用 entrySet 方法。 entrySet 方法是 Map 集合中的一个方法,它可以将 Map 集合中的所有键值对...

    Java-HashMap.rar_hashmap_java hashmap

    6. **迭代器遍历**:尽管遍历顺序不确定,但`HashMap`提供`keySet()`, `values()`和`entrySet()`方法来遍历键、值和键值对。 下面是一些关于`HashMap`的基本操作: - **插入键值对**:使用`put()`方法插入键值对,...

    java Map 遍历方法

    ### Java Map遍历方法详解 在Java编程语言中,`Map`接口是集合框架中的一个核心组成部分,它存储键值对映射。本篇文章将详细介绍几种常用的遍历`Map`的方法,包括使用传统迭代器(Iterator)、增强型for循环(For-...

    java中map的使用实例

    在Java编程语言中,Map接口是集合框架的重要组成部分,它提供了键值对(key-value pairs)的存储方式。Map不是列表或数组,而是允许我们通过一个键...理解并正确使用这些Map类可以帮助你编写更高效、更可读的Java代码。

    java遍历大容量map的正确方法.pdf

    在Java编程中,遍历大容量的Map是一个常见的操作,特别是在处理大数据或内存优化的场景下,选择正确的遍历方式至关重要。以下是对标题和描述中提及的四种遍历Map方法的详细说明: 1. **通过Map.keySet遍历key和...

    java xml和map互转

    在Java中,我们可以创建一个工具类来实现XML到Map的转换以及Map到XML的转换。 首先,我们需要在项目中引入DOM4J库。如果你使用Maven,可以在`pom.xml`文件中添加以下依赖: ```xml &lt;groupId&gt;dom4j ...

    java中Map映射机制

    总的来说,Java中的Map映射机制为存储和检索键值对提供了一种灵活且高效的方式,其各种实现和方法提供了丰富的功能以适应不同的编程需求。理解和掌握Map接口及其常用实现对于编写高效、健壮的Java代码至关重要。

    Java HashMap 如何正确遍历并删除元素的方法小结

    本文将介绍 Java HashMap 遍历和删除元素的正确方法。 一、HashMap 遍历方法 Java HashMap 提供了两种遍历方法:foreach 模式和迭代器遍历。 1. foreach 模式 foreach 模式是最简单的遍历方法,适用于不需要修改...

    java Map遍历方法

    Java提供了`Map`接口的`entrySet()`方法,返回一个包含`Map`中所有键值对的`Set`视图。通过这个`Set`,我们可以使用迭代器来遍历所有的键值对。 ```java Map, Integer&gt; map = new HashMap(); for (Map.Entry, ...

    java中Map的两种遍历方法

    本文将详细探讨在Java中遍历`Map`的两种常用方法:通过`keySet()`方法和通过`entrySet()`方法。 ### 一、通过`keySet()`方法遍历`Map` #### 方法概述 `keySet()`方法返回一个包含`Map`中所有键的`Set`视图。通过...

    java中map集合的用法

    - 键必须实现`hashCode()`和`equals()`方法,以确保正确地进行哈希计算和比较。 - 如果需要保持插入顺序,应使用LinkedHashMap;如果需要保持自然排序或自定义排序,应使用TreeMap。 - 当使用null键或值时,需注意...

    金陵科技学院软件院大二上Java高级1212Map.docx

    本教程将详细介绍如何使用Map以及特定实现如HashMap和TreeMap。 1. **定义Map并加入数据**: 在Java中,我们通常通过创建HashMap或TreeMap的实例来创建Map。例如,以下代码展示了如何创建一个HashMap,并插入一些...

    Hashmap实现了Map接口的底层实现.docx

    HashMap是Java编程语言中一种非常重要的数据结构,它实现了Map接口,允许存储键值对,且支持null键和null值。HashMap的底层实现基于数组和链表,这使得它具有较快的查找速度。以下是关于HashMap的详细说明: 一、...

    Map,HashMap,TreeMap的使用

    在 Java 中,HashMap、LinkedHashMap、TreeMap 都实现了 Map 接口,都是 Map 的子类,每个子类都有其特点和使用场景。 HashMap HashMap 是最常用的 Map 实现类,它根据键的哈希码值存储数据,能够快速地存储和获取...

    Java Map 集合类简介

    Java的核心类库提供了多种Map实现,如HashMap、TreeMap、LinkedHashMap、Hashtable等。HashMap是默认的无序、非同步实现,适合大多数情况;TreeMap基于红黑树,提供了有序的键值对,但性能略低于HashMap;...

Global site tag (gtag.js) - Google Analytics