Why are ArrayList created with empty elements array but HashSet with null table?

Imbar M. :

Maybe a bit of a philosophical question.

Looking at java's ArrayList implementation I noticed that when creating a new instance, the internal "elementData" array (that holds the items) is created as new empty array:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

However, a HashSet (that is based on a HashMap) is created with the table and entreySet are just left null;

transient Node<K,V>[] table;
transient Set<Map.Entry<K,V>> entrySet;

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

This got me thinking so I went and looked up C#'s List and HashSet: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,61f6a8d9f0c40f6e https://referencesource.microsoft.com/#System.Core/System/Collections/Generic/HashSet.cs,2d265edc718b158b

List:

static readonly T[]  _emptyArray = new T[0]; 

public List() {
        _items = _emptyArray;
}

HashSet:

private int[] m_buckets;

public HashSet()
        : this(EqualityComparer<T>.Default) { }

public HashSet(IEqualityComparer<T> comparer) {
    if (comparer == null) {
        comparer = EqualityComparer<T>.Default;
    }

    this.m_comparer = comparer;
    m_lastIndex = 0;
    m_count = 0;
    m_freeList = -1;
    m_version = 0;
}

So, is there a good reason why both languages picked empty for list and null for set/map?

They both used the "single instance" for the empty array trick, which is nice, but why not just have a null array?

Kevin Gosse :

Answering from a C# perspective.

For an empty ArrayList, you'll find that all the logic (get, add, grow, ...) works "as-is" if you have an empty array as backing store. No need for additional code to handle the uninitialized case, this makes the whole implementation neater. And since the empty array is cached, this does not result in an additional heap allocation, so you get the cleaner code at no extra cost.

For HashSet this is not possible, as accessing a bucket is done through the formula hashCode % m_buckets.Length. Trying to compute % 0 is considered as a division by 0, and therefore invalid. This means you need to handle specifically the "not initialized" case, so you gain nothing from pre-assigning the field with an empty array.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=151580&siteId=1