散列集添加方法无法正常

时间:2015-08-12 09:50:05

标签: java hashset

我写了一个虚拟程序,它在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表现得像这样。

3 个答案:

答案 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看起来像这样:

GrepCode