Java HashSet实现设计

时间:2015-03-27 19:58:15

标签: java oop hashset linkedhashset

编辑问题以删除我的类比并直接询问。 JDK HashSet实现如下:

public class HashSet {
  private HashMap map;
  public HashSet(int capacity) {
    map = new HashMap(capacity);
  }

  public HashSet(int capacity, float loadFactor) {
    map = new HashMap(capacity, loadFactor);
  }

  HashSet(int capacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(capacity, loadFactor);
  }
}

LinkedHashSet的实现方式如下:

public class LinkedHashSet
  extends HashSet {
  public LinkedHashSet(int capacity) {
    super(capacity, 0, true);
  }
  public LinkedHashSet(int capacity, float loadFactor) {
    super(capacity, loadFactor, true);
  }
}

班级HashSet中的第三个构造函数:HashSet(int capacity, loadFactor, boolean dummy)仅用于帮助LinkedHashSet班级使用LinkedHashMap作为支持地图而不是默认HashMap

问题:

  • 这会被认为是一个好设计吗?
  • 让子类指定支持map类型不是更好吗?
  • 我花了30分钟找到了JDK源代码,其中恰好是LinkedHashSet的双向链表,因为上面的实现范例并没有直观地发生在我身上,何时这样的设计是一个不错的选择呢? / LI>

1 个答案:

答案 0 :(得分:2)

你说这不是最好的设计选择。

总结:

HashSet有3个构造函数:

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * the specified initial capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hash map
 * @param      loadFactor        the load factor of the hash map
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero, or if the load factor is nonpositive
 */
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * the specified initial capacity and default load factor (0.75).
 *
 * @param      initialCapacity   the initial capacity of the hash table
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero
 */
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

/**
 * Constructs a new, empty linked hash set.  (This package private
 * constructor is only used by LinkedHashSet.) The backing
 * HashMap instance is a LinkedHashMap with the specified initial
 * capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hash map
 * @param      loadFactor        the load factor of the hash map
 * @param      dummy             ignored (distinguishes this
 *             constructor from other int, float constructor.)
 * @throws     IllegalArgumentException if the initial capacity is less
 *             than zero, or if the load factor is nonpositive
 */
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

最后一个有一个额外的参数dummy,它没有被使用,只是让编译器区分具有相同参数的2个构造函数。唯一的区别是更改支持地图的实施。

我们在对象设计方面做得更好,因为这些课程已经写完了。

如果今天重写了,可能会有一个构造函数改为使用Map,以便任何Map实现都可以用作后备存储:

HashSet(Map<K,V> backingMap);

和/或可能存在多个具有不同名称但与

相同的参数的静态工厂方法
 public static HashSet create(int initialCapacity, float loadFactor)

 public static HashSet createLinked(int initialCapacity, float loadFactor)

修改

JB Nizet提出了一个有趣的观点。当从ObjectInputStream重构一个对象时,HashSet的readObject()方法具有显式代码,以查看该实例是否属于LinkedHashSet。

// Create backing HashMap
    map = (((HashSet<?>)this) instanceof LinkedHashSet ?
           new LinkedHashMap<E,Object>(capacity, loadFactor) :
           new HashMap<E,Object>(capacity, loadFactor));

这只是真的可能,因为构造函数的伪参数版本是包私有的,因此这些是当前唯一的两种可能的实现。如果没有这种技术,您将无法正确使用ReadObject()。

这可能是Josh Bloch在Effective Java中编写序列化章节的原因。您可能必须使用SerializationProxy(Item 78)才能正确读取LinkedHashSet。