WeakHashMap 的 keySet 条目永远不会为空吗?

发布于 2024-11-10 18:49:07 字数 462 浏览 4 评论 0 原文

如果我迭代 WeakHashMap 的键集,是否需要检查 null 值?

WeakHashMap<MyObject, WeakReference<MyObject>> hm
    = new WeakHashMap<MyObject, WeakReference<MyObject>>();

for ( MyObject item : hm.keySet() ) {
    if ( item != null ) { // <- Is this test necessary?
        // Do something...
    } 
}

换句话说,当我迭代 WeakHashMap 的元素时,它们可以被收集吗?

编辑

对于这个问题,我们可以假设哈希映射中没有添加null条目。

If I iterate over the key set of a WeakHashMap, do I need to check for null values?

WeakHashMap<MyObject, WeakReference<MyObject>> hm
    = new WeakHashMap<MyObject, WeakReference<MyObject>>();

for ( MyObject item : hm.keySet() ) {
    if ( item != null ) { // <- Is this test necessary?
        // Do something...
    } 
}

In other words, can elements of the WeakHashMap be collected while I am iterating over them?

EDIT

For the sake of this question, one can assume that no null entries is added in the hash map.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

恬淡成诗 2024-11-17 18:49:07

我不熟悉 WeakHashMap,但您可能有一个 null 对象。看这个例子:

public static void main(String[] args)
{
    WeakHashMap<Object, WeakReference<Object>> hm
    = new WeakHashMap<Object, WeakReference<Object>>();
    hm.put(null, null);
    for ( Object item : hm.keySet() ) {
        if ( item == null ) { 
          System.out.println("null object exists");  
        } 
    }
}

I'm not familiar with WeakHashMap, but you might have one null object. see this example:

public static void main(String[] args)
{
    WeakHashMap<Object, WeakReference<Object>> hm
    = new WeakHashMap<Object, WeakReference<Object>>();
    hm.put(null, null);
    for ( Object item : hm.keySet() ) {
        if ( item == null ) { 
          System.out.println("null object exists");  
        } 
    }
}
尬尬 2024-11-17 18:49:07

再次来自 WeakHashMap javadoc

具有弱键的基于哈希表的 Map 实现。当 WeakHashMap 中的某个条目的键不再被日常使用时,该条目将自动被删除。更准确地说,给定键的映射的存在不会阻止该键被垃圾收集器丢弃,即使其可最终化、最终确定,然后回收。当一个键被丢弃时,它的条目实际上会从映射中删除,因此此类的行为与其他 Map 实现有些不同。

我读为:是的...当 WeakHaskMap 中没有对某个键的剩余外部引用时,那么该键可能会被GC,从而使关联的值无法访问,因此它(假设有)没有外部直接引用)适合 GC。

我将测试这个理论。这只是我对 doco 的解释...我没有任何 WeakHashMap 的经验...但我立即看到它作为“内存安全”对象缓存的潜力。

干杯。基思.


编辑:探索WeakHashMap...专门测试我的理论,即对特定键的外部引用将导致该键被保留...这是纯粹的废话;-)

我的测试工具:

package forums;

import java.util.Set;
import java.util.Map;
import java.util.WeakHashMap;
import krc.utilz.Random;

public class WeakCache<K,V> extends WeakHashMap<K,V>
{
  private static final int NUM_ITEMS = 2000;
  private static final Random RANDOM = new Random();

  private static void runTest() {
    Map<String, String> cache = new WeakCache<String, String>();
    String key; // Let's retain a reference to the last key object
    for (int i=0; i<NUM_ITEMS; ++i ) {
      /*String*/ key = RANDOM.nextString();
      cache.put(key, RANDOM.nextString());
    }

    System.out.println("There are " + cache.size() + " items of " + NUM_ITEMS + " in the cache before GC.");

    // try holding a reference to the keys
    Set<String> keys = cache.keySet();
    System.out.println("There are " + keys.size() + " keys");

    // a hint that now would be a good time to run the GC. Note that this
    // does NOT guarantee that the Garbage Collector has actually run, or
    // that it's done anything if it did run!
    System.gc();

    System.out.println("There are " + cache.size() + " items of " + NUM_ITEMS + " remaining after GC");
    System.out.println("There are " + keys.size() + " keys");
  }

  public static void main(String[] args) {
    try {
      for (int i=0; i<20; ++i ) {
        runTest();
        System.out.println();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

一次测试运行的(相当令人困惑,我认为)结果:

There are 1912 items of 2000 in the cache before GC.
There are 1378 keys
There are 1378 items of 2000 remaining after GC
There are 909 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 1961 items of 2000 remaining after GC
There are 1588 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 1936 items of 2000 remaining after GC
There are 1471 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1669 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1264 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1770 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1679 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1774 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1668 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1834 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 429 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

当我的代码正在执行时,按键似乎仍然消失......在 GC 提示后可能需要微睡眠。 . 给 GC 时间来做它的事情。无论如何,这种“波动”是有趣的行为。


编辑2:是的,直接在System.gc()之后添加行try{Thread.sleep(10);}catch(Exception e){} ; 使结果“更可预测”。

There are 1571 items of 2000 in the cache before GC.
There are 1359 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

.... and so on for 20 runs ...

嗯...当 GC 启动时,缓存就会完全消失...在真实应用程序中的任意时间...没有多大用处...嗯...我想知道 WeakHashMap 的用途是什么? ;-)


最后编辑,我保证

这是我的krc/utilz/Random(在上面的测试中使用)

package krc.utilz;

import java.io.Serializable;
import java.nio.charset.Charset;

/**
 * Generates random values. Extends java.util.Random to do all that plus:<ul>
 * <li>generate random values in a given range, and
 * <li>generate Strings of random characters and random length.
 * </ul>
 * <p>
 * Motivation: I wanted to generate random Strings of random length for test 
 *  data in some jUnit tests, and was suprised to find no such ability in the
 *  standard libraries... so I googled it, and came up with Glen McCluskey's
 *  randomstring function at http://www.glenmccl.com/tip_010.htm. Then I thought
 *  aha, that's pretty cool, but if we just extended it a bit, and packaged it
 *  properly then it'd be useful, and reusable. Cool!
 * See: http://www.glenmccl.com/tip_010.htm
 * See: http://forum.java.sun.com/thread.jspa?threadID=5117756&messageID=9406164
 */
public class Random extends java.util.Random  implements Serializable
{

  private static final long serialVersionUID = 34324;
  public static final int DEFAULT_MIN_STRING_LENGTH = 5;
  public static final int DEFAULT_MAX_STRING_LENGTH = 25;

  public Random() {
    super();
  }

  public Random(long seed) {
    super(seed);
  }

  public double nextDouble(double lo, double hi) {
    double n = hi - lo;
    double i = super.nextDouble() % n;
    if (i < 0) i*=-1.0;
    return lo + i;
  }

  /**
   * @returns a random int between lo and hi, inclusive.
   */
  public int nextInt(int lo, int hi) 
    throws IllegalArgumentException
  {
    if(lo >= hi) throw new IllegalArgumentException("lo must be < hi");
    int n = hi - lo + 1;
    int i = super.nextInt() % n;
    if (i < 0) i = -i;
    return lo + i;
  }

  /**
   * @returns a random int between lo and hi (inclusive), but exluding values
   *  between xlo and xhi (inclusive).
   */
  public int nextInt(int lo, int hi, int xlo, int xhi) 
    throws IllegalArgumentException
  {
    if(xlo < lo) throw new IllegalArgumentException("xlo must be >= lo");
    if(xhi > hi) throw new IllegalArgumentException("xhi must be =< hi");
    if(xlo > xhi) throw new IllegalArgumentException("xlo must be >= xhi");
    int i;
    do {
      i = nextInt(lo, hi);
    } while(i>=xlo && i<=xhi);
    return(i);
  }

  /**
   * @returns a string (of between 5 and 25 characters, inclusive) 
   *  consisting of random alpha-characters [a-z]|[A-Z].
   */
  public String nextString()
    throws IllegalArgumentException
  {
    return(nextString(DEFAULT_MIN_STRING_LENGTH, DEFAULT_MAX_STRING_LENGTH));
  }

  /**
   * @returns a String (of between minLen and maxLen chars, inclusive) 
   *  which consists of random alpha-characters. The returned string matches
   *  the regex "[A-Za-z]{$minLen,$maxLan}". 
   * @nb: excludes the chars "[\]^_`" between 'Z' and 'a', ie chars (91..96).
   * @see: http://www.neurophys.wisc.edu/comp/docs/ascii.html
   */
  public String nextString(int minLen, int maxLen)
    throws IllegalArgumentException
  {
    if(minLen < 0) throw new IllegalArgumentException("minLen must be >= 0");
    if(minLen > maxLen) throw new IllegalArgumentException("minLen must be <= maxLen");
    return(nextString(minLen, maxLen, 'A', 'z', '[', '`'));
  }

  /**
   * @does: generates a String (of between minLen and maxLen chars, inclusive) 
   *  which consists of characters between lo and hi, inclusive.
   */
  public String nextString(int minLen, int maxLen, char lo, char hi)
    throws IllegalArgumentException
  {
    if(lo < 0) throw new IllegalArgumentException("lo must be >= 0");
    String retval = null;
    try {
      int n = minLen==maxLen ? maxLen : nextInt(minLen, maxLen);
      byte b[] = new byte[n];
      for (int i=0; i<n; i++)
        b[i] = (byte)nextInt((int)lo, (int)hi);
      retval = new String(b, Charset.defaultCharset().name());
    } catch (Exception e) {
      e.printStackTrace();
    }
    return retval;
  }

  /**
   * @does: generates a String (of between minLen and maxLen chars, inclusive) 
   *  which consists of characters between lo and hi, inclusive, but excluding
   *  character between 
   */
  public String nextString(int minLen, int maxLen, char lo, char hi, char xlo, char xhi) 
    throws IllegalArgumentException
  {
    if(lo < 0) throw new IllegalArgumentException("lo must be >= 0");
    String retval = null;
    try {
      int n = minLen==maxLen ? maxLen : nextInt(minLen, maxLen);
      byte b[] = new byte[n];
      for (int i=0; i<n; i++) {
        b[i] = (byte)nextInt((int)lo, (int)hi, (int)xlo, (int)xhi);
      }
      retval = new String(b, Charset.defaultCharset().name());
    } catch (Exception e) {
      e.printStackTrace();
    }
    return retval;
  }

}

Again from WeakHashMap javadoc:

A hashtable-based Map implementation with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.

Which I read as: Yep... When there are no remaining external references to a Key in WeakHaskMap, then that Key maybe GC'd, making the associated Value unreachable, so it to (presuming there are no external references directly to it) is elligible for GC.

I'm going to test this theory. It's only my interpretation of the doco... I don't have any experience with WeakHashMap... but I immediately see it's potential as "memory-safe" object-cache.

Cheers. Keith.


EDIT: Exploring WeakHashMap... specifically testing my theory that an external-references to the particular key would cause that key to be retained... which is pure bunkum ;-)

My test harness:

package forums;

import java.util.Set;
import java.util.Map;
import java.util.WeakHashMap;
import krc.utilz.Random;

public class WeakCache<K,V> extends WeakHashMap<K,V>
{
  private static final int NUM_ITEMS = 2000;
  private static final Random RANDOM = new Random();

  private static void runTest() {
    Map<String, String> cache = new WeakCache<String, String>();
    String key; // Let's retain a reference to the last key object
    for (int i=0; i<NUM_ITEMS; ++i ) {
      /*String*/ key = RANDOM.nextString();
      cache.put(key, RANDOM.nextString());
    }

    System.out.println("There are " + cache.size() + " items of " + NUM_ITEMS + " in the cache before GC.");

    // try holding a reference to the keys
    Set<String> keys = cache.keySet();
    System.out.println("There are " + keys.size() + " keys");

    // a hint that now would be a good time to run the GC. Note that this
    // does NOT guarantee that the Garbage Collector has actually run, or
    // that it's done anything if it did run!
    System.gc();

    System.out.println("There are " + cache.size() + " items of " + NUM_ITEMS + " remaining after GC");
    System.out.println("There are " + keys.size() + " keys");
  }

  public static void main(String[] args) {
    try {
      for (int i=0; i<20; ++i ) {
        runTest();
        System.out.println();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

The (rathering perplexing, I think) results of one test-run:

There are 1912 items of 2000 in the cache before GC.
There are 1378 keys
There are 1378 items of 2000 remaining after GC
There are 909 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 1961 items of 2000 remaining after GC
There are 1588 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 1936 items of 2000 remaining after GC
There are 1471 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1669 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1264 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1770 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1679 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1774 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1668 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 1834 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 2000 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 429 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

It would appear that keys are still disappearing WHILE my code is executing... possibly a micro-sleep is required after the GC-hint... to give the GC time to do it's stuff. Anyway, this "volatility" is interesting behaviour.


EDIT 2: Yup, adding the line try{Thread.sleep(10);}catch(Exception e){} directly after the System.gc(); makes the results "more predictable".

There are 1571 items of 2000 in the cache before GC.
There are 1359 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

There are 2000 items of 2000 in the cache before GC.
There are 2000 keys
There are 0 items of 2000 remaining after GC
There are 0 keys

.... and so on for 20 runs ...

Hmmm... A cache that just completely disappears when the GC kicks in... at arbitrary times in a real app... not much use... Hmmm... What is WeakHashMap for I wonder? ;-)


Last EDIT, I promise

Here's my krc/utilz/Random (used in the above test)

package krc.utilz;

import java.io.Serializable;
import java.nio.charset.Charset;

/**
 * Generates random values. Extends java.util.Random to do all that plus:<ul>
 * <li>generate random values in a given range, and
 * <li>generate Strings of random characters and random length.
 * </ul>
 * <p>
 * Motivation: I wanted to generate random Strings of random length for test 
 *  data in some jUnit tests, and was suprised to find no such ability in the
 *  standard libraries... so I googled it, and came up with Glen McCluskey's
 *  randomstring function at http://www.glenmccl.com/tip_010.htm. Then I thought
 *  aha, that's pretty cool, but if we just extended it a bit, and packaged it
 *  properly then it'd be useful, and reusable. Cool!
 * See: http://www.glenmccl.com/tip_010.htm
 * See: http://forum.java.sun.com/thread.jspa?threadID=5117756&messageID=9406164
 */
public class Random extends java.util.Random  implements Serializable
{

  private static final long serialVersionUID = 34324;
  public static final int DEFAULT_MIN_STRING_LENGTH = 5;
  public static final int DEFAULT_MAX_STRING_LENGTH = 25;

  public Random() {
    super();
  }

  public Random(long seed) {
    super(seed);
  }

  public double nextDouble(double lo, double hi) {
    double n = hi - lo;
    double i = super.nextDouble() % n;
    if (i < 0) i*=-1.0;
    return lo + i;
  }

  /**
   * @returns a random int between lo and hi, inclusive.
   */
  public int nextInt(int lo, int hi) 
    throws IllegalArgumentException
  {
    if(lo >= hi) throw new IllegalArgumentException("lo must be < hi");
    int n = hi - lo + 1;
    int i = super.nextInt() % n;
    if (i < 0) i = -i;
    return lo + i;
  }

  /**
   * @returns a random int between lo and hi (inclusive), but exluding values
   *  between xlo and xhi (inclusive).
   */
  public int nextInt(int lo, int hi, int xlo, int xhi) 
    throws IllegalArgumentException
  {
    if(xlo < lo) throw new IllegalArgumentException("xlo must be >= lo");
    if(xhi > hi) throw new IllegalArgumentException("xhi must be =< hi");
    if(xlo > xhi) throw new IllegalArgumentException("xlo must be >= xhi");
    int i;
    do {
      i = nextInt(lo, hi);
    } while(i>=xlo && i<=xhi);
    return(i);
  }

  /**
   * @returns a string (of between 5 and 25 characters, inclusive) 
   *  consisting of random alpha-characters [a-z]|[A-Z].
   */
  public String nextString()
    throws IllegalArgumentException
  {
    return(nextString(DEFAULT_MIN_STRING_LENGTH, DEFAULT_MAX_STRING_LENGTH));
  }

  /**
   * @returns a String (of between minLen and maxLen chars, inclusive) 
   *  which consists of random alpha-characters. The returned string matches
   *  the regex "[A-Za-z]{$minLen,$maxLan}". 
   * @nb: excludes the chars "[\]^_`" between 'Z' and 'a', ie chars (91..96).
   * @see: http://www.neurophys.wisc.edu/comp/docs/ascii.html
   */
  public String nextString(int minLen, int maxLen)
    throws IllegalArgumentException
  {
    if(minLen < 0) throw new IllegalArgumentException("minLen must be >= 0");
    if(minLen > maxLen) throw new IllegalArgumentException("minLen must be <= maxLen");
    return(nextString(minLen, maxLen, 'A', 'z', '[', '`'));
  }

  /**
   * @does: generates a String (of between minLen and maxLen chars, inclusive) 
   *  which consists of characters between lo and hi, inclusive.
   */
  public String nextString(int minLen, int maxLen, char lo, char hi)
    throws IllegalArgumentException
  {
    if(lo < 0) throw new IllegalArgumentException("lo must be >= 0");
    String retval = null;
    try {
      int n = minLen==maxLen ? maxLen : nextInt(minLen, maxLen);
      byte b[] = new byte[n];
      for (int i=0; i<n; i++)
        b[i] = (byte)nextInt((int)lo, (int)hi);
      retval = new String(b, Charset.defaultCharset().name());
    } catch (Exception e) {
      e.printStackTrace();
    }
    return retval;
  }

  /**
   * @does: generates a String (of between minLen and maxLen chars, inclusive) 
   *  which consists of characters between lo and hi, inclusive, but excluding
   *  character between 
   */
  public String nextString(int minLen, int maxLen, char lo, char hi, char xlo, char xhi) 
    throws IllegalArgumentException
  {
    if(lo < 0) throw new IllegalArgumentException("lo must be >= 0");
    String retval = null;
    try {
      int n = minLen==maxLen ? maxLen : nextInt(minLen, maxLen);
      byte b[] = new byte[n];
      for (int i=0; i<n; i++) {
        b[i] = (byte)nextInt((int)lo, (int)hi, (int)xlo, (int)xhi);
      }
      retval = new String(b, Charset.defaultCharset().name());
    } catch (Exception e) {
      e.printStackTrace();
    }
    return retval;
  }

}
梦屿孤独相伴 2024-11-17 18:49:07

假设您没有将 null 键值插入到 WeakHashMap 中,则您不需要需要检查迭代的键值是否为迭代键集时null。在迭代条目集时,您也不需要需要检查在迭代的 Map.Entry 实例上调用的 getKey() 是否为 null 。两者都由文档保证,但有点间接;它是 Iterator.hasNext 的合同() 提供这些保证。

WeakHashMap 的 JavaDoc< /a> 状态:

WeakHashMap 中的每个键对象都间接存储为弱引用的指示对象。因此,只有在垃圾收集器清除了地图内部和外部对键的弱引用后,键才会自动被删除。

Iterator.hasNext() 的 JavaDoc 指出:

如果迭代有更多元素,则返回true。 (换句话说,如果 true rel="nofollow noreferrer">next() 将返回一个元素而不是抛出异常。)

因为键集和条目集视图满足 Set 约定(根据 Set 的要求) code>Map 合约其中WeakHashMap 实现),由 Set.iterator() 方法必须满足 Iterator 契约。

当 hasNext() 返回 true 时,Iterator 合约要求对 Iterator 实例上的 next() 的下一次调用必须返回有效值。 WeakHashMap 满足 Iterator 契约的唯一方法是让 hasNext() 实现在返回 true,从而防止垃圾收集器清除对 WeakHashMap 所持有的键值的弱引用,从而防止该条目自动从 WeakHashMap< 中删除/code> 这样 next()有返回值。

事实上,如果您查看 WeakHashMap 的源代码,您会发现 HashIterator 内部类(由键、值和条目迭代器实现使用)具有一个 currentKey 字段保存对当前键值的强引用,nextKey 字段保存对下一个键值的强引用。 currentKey 字段允许 HashIterator 实现 Iterator.remove() 完全符合该方法的约定。 nextKey 字段允许 HashIterator 满足 hasNext() 的约定。

话虽这么说,假设您想通过调用 toArray() 来收集映射中的所有键值,然后迭代此键值快照。有几种情况需要考虑:

  1. 如果调用返回 Object[] 的无参数 toArray() 方法或传入零长度数组,如下所示:

    最终集 items = hm.keySet();
    for (final MyObject item : items.toArray(new MyObject[0])) {
        // 做点什么...
    }
    

    .. 那么您不需要需要检查item是否为null,因为在这两种情况下,返回的数组都将被修剪以保存迭代器返回的元素的确切数量。

  2. 如果您传入一个长度 >= WeakHashMap 的当前大小的数组,如下所示:

    最终集 items = hm.keySet();
    for (final MyObject item : items.toArray(new MyObject[items.size()])) {
        if (null == 项目) {
            休息;
        }
        // 做点什么...
    }
    

    .. 那么null检查必要的。原因是,在 size() 返回一个值(用于创建存储键的数组)和 toArray() 完成对 WeakHashMap 的键的迭代之间的时间里,条目可能已被自动删除。这是 Collection.toArray() 的 JavaDoc

    <块引用>

    如果此集合适合指定数组且有剩余空间(即,该数组的元素多于此集合),则数组中紧随集合末尾的元素将设置为 null。 (仅当调用者知道此集合不包含任何 null 元素时,这才有助于确定此集合的长度。)

    因为您知道您尚未将 null 键值插入到 WeakHashMap 中,所以您可以在看到第一个 null 值时中断(如果您看到 null)。

  3. 与前一种情况略有不同,如果您传入一个非零长度的数组,那么您需要 null 检查,因为“有空闲空间”情况可能在运行时发生。

Assuming that you don't insert a null key value into a WeakHashMap, you do not need to check whether the key value of the iteration is null when iterating through the key set. You also do not need to check whether getKey() called on the Map.Entry instance of the iteration is null when iterating through the entry set. Both are guaranteed by the documentation, but it's somewhat indirect; it is the contract of Iterator.hasNext() that provides these guarantees.

The JavaDoc for WeakHashMap states:

Each key object in a WeakHashMap is stored indirectly as the referent of a weak reference. Therefore a key will automatically be removed only after the weak references to it, both inside and outside of the map, have been cleared by the garbage collector.

The JavaDoc for Iterator.hasNext() states:

Returns true if the iteration has more elements. (In other words, returns true if next() would return an element rather than throwing an exception.)

Because the key set and entry set views satisfy the Set contract (as required by the Map contract which WeakHashMap implements), the iterators returned by the Set.iterator() method must satisfy the Iterator contract.

When hasNext() returns true, the Iterator contract requires that the next call to next() on the Iterator instance must return a valid value. The only way for WeakHashMap to satisfy the Iterator contract is for the hasNext() implementation to keep a strong reference to the next key when it returns true, thereby preventing the weak reference to the key value held by the WeakHashMap from being cleared by the garbage collector, and, as a consequence, preventing the entry from being automatically removed from the WeakHashMap so that next() has a value to return.

Indeed, if you look at the source of WeakHashMap, you will see that the HashIterator inner class (used by the key, value, and entry iterator implementations) has a currentKey field that holds a strong reference to the current key value and a nextKey field that holds a strong reference to the next key value. The currentKey field allows HashIterator to implement Iterator.remove() in full compliance with that method's contract. The nextKey field allows HashIterator to satisfy the contract of hasNext().

That being said, suppose you want to gather up all key values in the map by calling toArray(), and then iterate through this snapshot of key values. There are a few cases to consider:

  1. If you call the no-argument toArray() method that returns Object[] or pass in a zero-length array, as in:

    final Set<MyObject> items = hm.keySet();
    for (final MyObject item : items.toArray(new MyObject[0])) {
        // Do something...
    }
    

    .. then you do not need to check whether item is null because in both cases, the array returned will be trimmed to hold the exact number of elements that were returned by the iterator.

  2. If you pass in an array of length >= the WeakHashMap's then-current size, as in:

    final Set<MyObject> items = hm.keySet();
    for (final MyObject item : items.toArray(new MyObject[items.size()])) {
        if (null == item) {
            break;
        }
        // Do something...
    }
    

    .. then the null check is necessary. The reason is that, in the time between when size() returned a value (which is used to create the array to store the keys) and toArray() finishes iterating through the keys of the WeakHashMap, an entry may have been automatically removed. This is the "room to spare" case mentioned in the JavaDoc for Collection.toArray():

    If this collection fits in the specified array with room to spare (i.e., the array has more elements than this collection), the element in the array immediately following the end of the collection is set to null. (This is useful in determining the length of this collection only if the caller knows that this collection does not contain any null elements.)

    Because you know that you have not inserted a null key value into the WeakHashMap, you can break upon seeing the first null value (if you see a null).

  3. A slight variant of the previous case, if you pass in an array of non-zero length, then you need the null check for the reason that the "room to spare" case might happen at runtime.

缱绻入梦 2024-11-17 18:49:07

WeakHashMap 允许 null 作为键和值。您可以添加空键和值。 因此,如果您没有插入空条目,则不需要添加空检查

WeakHashMap permits null as key and value. You can add null key and value. So in case, if you have not inserted null entries, then you don't need to add the null check

烟花易冷人易散 2024-11-17 18:49:07

From the WeakHashMap documentation, the key that is placed into the hash map is of a templated type, which means it is inherited from java.lang.object. As a result, it may be null. So, a key may be null.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文