我有一些代码创建一个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:
当迭代它时,用户必须手动同步返回的集合:
Run Code Online (Sandbox Code Playgroud)Set s = Collections.synchronizedSet(new HashSet()); ... synchronized(s) { Iterator i = s.iterator(); // Must be in the synchronized block while (i.hasNext()) foo(i.next()); }不遵循此建议可能会导致非确定性行为.
对我来说,非确定性行为可能包括-1的大小.我需要返回并确保在迭代集合时正确同步,但我怀疑这是问题所在.
根据文档,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.
也许它与代码的"......"部分有关?
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)
| 归档时间: |
|
| 查看次数: |
3374 次 |
| 最近记录: |