java Map可以返回大小-1吗?

mmo*_*son 5 java

我有一些代码创建一个List,用Map的大小初始化:

private Set<String> mKeys = new HashSet<String>(64);
....
List<String> keyList = new ArrayList<String>(mKeys.size());
Run Code Online (Sandbox Code Playgroud)

我看到一个异常:java.lang.IllegalArgumentException:非法容量:-1

Map可以返回-1的大小吗?我正在查看HashSet的源代码,它由HashMap支持.HashMap的源代码显示了内部,其中elementCount总是在removeEntry()调用上递减.此外,HashMap.empty()的方法对elementCount的回复为== 0,如果elementCount为-1​​,则返回false.

有没有人遇到过这个?我可以围绕它进行编码,但这感觉就像一个黑客,这让我觉得我对当前的代码做错了.

编辑:我原本试图简化问题.我正在使用的Set实际上被定义为

private static Set<String> mKeys = Collections.synchronizedSet(new HashSet<String>(64));
Run Code Online (Sandbox Code Playgroud)

编辑:这里的关键可能在synchronizedSet中.来自JavaDoc:

当迭代它时,用户必须手动同步返回的集合:

Set s = Collections.synchronizedSet(new HashSet());
      ...
synchronized(s) {
    Iterator i = s.iterator(); // Must be in the synchronized block
    while (i.hasNext())
        foo(i.next());
}
Run Code Online (Sandbox Code Playgroud)

不遵循此建议可能会导致非确定性行为.

对我来说,非确定性行为可能包括-1的大小.我需要返回并确保在迭代集合时正确同步,但我怀疑这是问题所在.

Viv*_*sse 5

根据文档,HashMap返回地图中元素的数量,没有其他条件.负数的元素没有任何意义.

我想到的唯一可能的解释是大小为Integer.MAX_VALUE + 1的Map,这将导致负大小.但是AbstractMap#size()确切地说,如果Map大小大于Integer.MAX_VALUE,则返回Integer.MAX_VALUE,所以这种情况不会发生.

另外,我在我的计算机上尝试了如下代码:

Set<String> mKeys = new HashSet<String>(64);
System.out.println(mKeys.size());
Run Code Online (Sandbox Code Playgroud)

我得到了预期的结果:0.

也许它与代码的"......"部分有关?


A K*_*nin 5

HashSet size() 方法可以在多线程环境中返回负整数。下面的测试 1 将抛出一堆与负大小相关的异常。测试 2 是线程安全的方法,这是避免这种情况的解决方案之一。

编辑: 注意:这似乎是 Java 1.6 和旧版本中的一个问题。在 Java 1.7 中进行测试时,HashSet 不会返回负大小。如果未找到对象且未从 HashSet 中删除任何内容,则大小不会更改。

测试 1

package test;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

class TestHashSet1 {
    private static int NUMBER_OF_THREADS = 5;
    private static int COLLECTION_SIZE = 1000;

    public static void main(String[] arg) {
        final Set<Integer> toRemoveElement = new HashSet<Integer>();
        final Set<Integer> toStoreElements = new HashSet<Integer>();
        // populate collections for test
        for (int i = 0; i < COLLECTION_SIZE; i++) {
            Integer obj = new Integer(i);
            toRemoveElement.add(obj);
            toStoreElements.add(obj);
        }
        // two threads that will be using collection2 
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (Integer o : toStoreElements) {
                    removeObject(toRemoveElement, o);
                }
            }
        };
        Thread[] treads = new Thread[NUMBER_OF_THREADS];
        for (int i = 0; i < treads.length; i++) {
            treads[i] = new Thread(runnable);
        }
        for (Thread t : treads) {
            t.start();
        }
    }

    private static void removeObject(Set<Integer> toRemoveElement, Integer o) {
        toRemoveElement.remove(o);
        int size = toRemoveElement.size();
        if (size < 0) {
            System.out.println(size);
            try {
                toRemoveElement.toArray(new Integer[toRemoveElement.size()]);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

测试 2 - 线程安全

package test;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

class TestHashSet2 {
    private static int NUMBER_OF_THREADS = 5;
    private static int COLLECTION_SIZE = 1000;

    public static void main(String[] arg) {
        final Set<Integer> toRemoveElement = Collections.synchronizedSet(new HashSet<Integer>()); // example of collection sync
        final Set<Integer> toStoreElements = new HashSet<Integer>();
        // populate collections for test
        for (int i = 0; i < COLLECTION_SIZE; i++) {
            Integer obj = new Integer(i);
            toRemoveElement.add(obj);
            toStoreElements.add(obj);
        }
        // two threads that will be using collection2 
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (Integer o : toStoreElements) {
                    removeObject(toRemoveElement, o);
                }
            }
        };
        Thread[] treads = new Thread[NUMBER_OF_THREADS];
        for (int i = 0; i < treads.length; i++) {
            treads[i] = new Thread(runnable);
        }
        for (Thread t : treads) {
            t.start();
        }
    }

    synchronized private static void removeObject(Set<Integer> toRemoveElement, Integer o) { // example of method sync
        toRemoveElement.remove(o);
        int size = toRemoveElement.size();
        if (size < 0) {
            System.out.println(size);
            try {
                toRemoveElement.toArray(new Integer[toRemoveElement.size()]);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)