我写了一个虚拟程序,它在Hash Set中添加了对象。我创造了一辆可容纳5人的汽车。
现在问题是我从不同的主程序中得到了不同的结果。
请在下面找到2-Main节目。
第一个主要计划是
public class Main_1 {
static int counter = 0;
public static void main(String args[]) {
Car car = new Car();
for (int i = 0; i < 20; i++) {
car.add(new Person());
}
car.done();
}
}
Main_1的输出是:线程“main”中的异常java.lang.IllegalStateException:我已经满了 在Car.add(Car.java:10) 在Main_1.main(Main_1.java:8)
第二主程序是
public class Main_2 {
static int counter = 0;
static Car car = new Car();
public static void main(String args[]) {
car.add(new RecursivePerson());
car.done();
}
static class RecursivePerson extends Person {
public int hashCode() {
if (++counter < 20) {
car.add(new RecursivePerson());
}
return super.hashCode();
}
}
}
Main_2的输出是我是一辆有20人的车!
以下是我的计划的业务逻辑。
import java.util.HashSet;
import java.util.Set;
public class Car {
private static final int CAPACITY = 5;
private Set<Person> people = new HashSet<Person>();
public synchronized void add(Person p) {
if (people.size() >= CAPACITY) {
throw new IllegalStateException("I'm full");
} else {
people.add(p);
}
}
public synchronized void done() {
if (people.size() == 20) {
// The goal is to reach this line
System.out.println("I'm a car with 20 people!");
}
}
}
class Person {
}
有人可以告诉我为什么java表现得像这样。
答案 0 :(得分:4)
不同之处在于HashSet
的工作方式:如果你向它添加一个新元素,它首先会检查对象是否已经在集合中,如果它不在,它是不是将此添加到集合中。为了检查对象是否在集合中,它会在对象上调用hashCode()
。
您的第二个程序专门用于绕过汽车的容量检查。您在添加到哈希集的对象中覆盖hashCode()
。此方法由HashSet.add
方法调用,但在对象实际添加到集合之前调用。在重写的hashCode()
方法中,您将其他元素添加到集合中。也就是说,如果调用Car.add()
,则哈希集的大小始终为0,并且容量检查将始终通过。
答案 1 :(得分:0)
HashSets使用HashMap实现。根据{{3}},让我们看一下源HashSet的添加源:
public boolean add(E e)
{
return map.put(e, PRESENT)==null;
}
根据GrepCode:
,让我们按照HashMap中的put实现进行操作public V More ...put(K key, V value)
{
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next)
{
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
{
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
}
使用addEntry将对象添加到最后一行。但是,在添加条目之前,在第3行中调用hashCode;最后,这个原因再次被召唤。因为你的hashCode方法会添加,直到你有20个元素,最后集合的大小为20。
答案 2 :(得分:0)
这是因为在Main_2
中你以递归方式调用add。对.add()
方法的初始调用不会返回实际增加人员列表的大小。所以,people.size()
所以它总会返回0,尽管你在那里添加了很多元素。
如果你进行一些调试,你会在Main_2
中进行几次迭代后看到callstack看起来像这样: