java - как да създадете персонализиран итератор на хеш-таблица?

В момента се опитвам да внедря колекция от Hashtable - имам всичко готово и работещо, но се натъкнах на концептуален проблем, когато се опитвах да дефинирам персонализиран итератор за таблицата. Имам вътрешен клас, наречен „HashEntry“, който са действителните обекти, съхранени в масива – те съхраняват ключа, стойността и състоянието на записа, т.е. празен, активен, изтрит.

private class HashEntry
{
    private TKey m_key;
    private TValue m_value;
    private EntryStatus status;

    //standard constructor
    public HashEntry(TKey key, TValue value)
    {
        m_key = key;
        m_value = value;
        status = EntryStatus.ACTIVE;
    }

    public HashEntry(TKey key, TValue value, EntryStatus i) {
        m_key = key;
        m_value = value;
        status = i;
    }

    //default 'empty' constructor
    public HashEntry()
    {
        //calls default constructor, creates placeholder entry
        m_key = null;
        m_value = null;
        status = EntryStatus.EMPTY;
    }

    //equals operator override, this override just compares compares
    // the objects held in the entry, so any object used with this
    // implementation must hae=ve its own equals override
    @Override
    public boolean equals(Object obj)
    {
        if (obj == null) { return false; }
        if (getClass() != obj.getClass()) { return false; }

        final HashEntry other = (HashEntry) obj;
        return (!((this.m_key == null) ? (other.m_key != null) : !this.m_key.equals(other.m_key)));
    }

    // override of the hashCode() function--just calls the hashCode
    // function of the embedded object, so that must be provided
    @Override
    public int hashCode()
    {
        return this.m_key.hashCode();
    }

    // toString override just returns the toString of the embedded object
    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(m_key.toString()).append(m_value.toString());
        return sb.toString();
    }
}

Това е първата част от въпроса ми - Ако искам да мога да итерирам през таблицата, трябва ли да итерирам през (и следователно да връщам) HashEntry обекти, или е конвенция за хеш таблица да итерирам през действителната стойност, съхранена в таблицата ? Класът HashEntry е частен, така че предполагам, че е лоша практика да връща негови екземпляри...

Но ако случаят е такъв, как да създам итератор на Hashtable, който итерира през своите обекти на HashEntrys? Трябва ли да дефинирам итератор/iterable в клас HashEntry?


person Ben Granger    schedule 08.06.2015    source източник
comment
Стандартната реализация на hashmap не запазва реда на вмъкване, сигурни ли сте, че вашият алгоритъм го прави?   -  person Elliott Frisch    schedule 08.06.2015
comment
Не съм наистина загрижен за реда на вмъкване (който алгоритъмът не запазва - итераторът наистина би бил само за вътрешна употреба, така че да мога да пиша методи, които по същество изпускат таблицата чисто, както и да итерирам във всеки друг метод .   -  person Ben Granger    schedule 08.06.2015
comment
Поне направете HashEntry общо, като HashEntry<TKey, TValue>.   -  person Elliott Frisch    schedule 08.06.2015


Отговори (1)


Най-общо казано, да, вероятно би било по-добре, ако предоставите итератор, който итерира над HashEntrys, така че потребителите да получават както ключа, така и стойността (и състоянието) при итерация. Често стойността няма да има смисъл без ключа и обратното.

Защо просто не направите HashEntry класа public static общ вътрешен клас и не направите специфични за изпълнението неща private? Вероятно ще трябва да направите и HashEntry общ, защото предполагам, че вашият родителски клас (нека го наречем просто MyHashTable) също е общ въз основа на TKey и TValue.

Така че, ако бях на твое място, щях да направя така, че HashEntry и MyHashTable да изглеждат по-скоро така:

// Note: implements Iterable<E> now
public class MyHashTable<TKey, TValue> implements Iterable<MyHashTable.HashEntry<TKey, TValue>>
{
    public Iterator<MyHashTable.HashEntry<TKey, TValue>> iterator() {
        // ...
        // Make and return your iterator here
        // ...
    }

    // Note: public and generic now
    public static class HashEntry<TKey, TValue>
    {
        private TKey m_key;
        private TValue m_value;
        private EntryStatus status;

        //standard constructor
        // Note: private now
        public HashEntry(TKey key, TValue value)
        {
            m_key = key;
            m_value = value;
            status = EntryStatus.ACTIVE;
        }

        // Note: private now
        private HashEntry(TKey key, TValue value, EntryStatus i) {
            m_key = key;
            m_value = value;
            status = i;
        }

        //default 'empty' constructor
        // Note: private now
        public HashEntry()
        {
            //calls default constructor, creates placeholder entry
            m_key = null;
            m_value = null;
            status = EntryStatus.EMPTY;
        }

        public TKey getKey() {
            return m_key;
        }

        public TValue getValue() {
            return m_value;
        }

        public EntryStatus getEntryStatus() {
            return status;
        }

        //equals operator override, this override just compares compares
        // the objects held in the entry, so any object used with this
        // implementation must hae=ve its own equals override
        @Override
        public boolean equals(Object obj)
        {
            if (obj == null) { return false; }
            if (getClass() != obj.getClass()) { return false; }

            final HashEntry other = (HashEntry) obj;
            return (!((this.m_key == null) ? (other.m_key != null) : !this.m_key.equals(other.m_key)));
        }

        // override of the hashCode() function--just calls the hashCode
        // function of the embedded object, so that must be provided
        @Override
        public int hashCode()
        {
            return this.m_key.hashCode();
        }

        // toString override just returns the toString of the embedded object
        @Override
        public String toString()
        {
            StringBuilder sb = new StringBuilder();
            sb.append(m_key.toString()).append(m_value.toString());
            return sb.toString();
        }
    }
}

Имайте предвид, че HashEntry сега е вътрешен клас на MyHashTable, той е общ и неговите конструктори сега са private. Това гарантира, че никой освен този външен клас MyHashTable не може да създаде екземпляр на HashEntry, защото инстанцирането на такъв извън вашата хеш таблица не би имало смисъл (вижте това). Въпреки това, други хора могат да получат достъп до ключовете и стойностите на записа чрез гетери.

Самият итератор би бил екземпляр на Iterator<MyHashTable.HashEntry<TKey, TValue>>. Що се отнася до писането на такава, това зависи от вашата собствена реализация на хеш-таблица, но вие основно се нуждаете от начин да получите следващия елемент в каквато и да е последователност: Iterator<E>.next().

Например, тук е реализация на метод iterator(), която итерира върху прост масив:

private Type[] arrayList;
private int currentSize;

@Override
public Iterator<Type> iterator() {
    Iterator<Type> it = new Iterator<Type>() {

        private int currentIndex = 0;

        @Override
        public boolean hasNext() {
            return currentIndex < currentSize && arrayList[currentIndex] != null;
        }

        @Override
        public Type next() {
            return arrayList[currentIndex++];
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    };
    return it;
}

(източник: https://stackoverflow.com/a/5849625/837703)

Надявам се, че това помогна малко.

person Community    schedule 08.06.2015
comment
Отлично, благодаря @dudeprgm, не бях мислил да направя класа HashEntry публичен, въпреки че вероятно ще трябва да дам някои бележки за това как трябва да се използва, сега, когато може да бъде. Благодаря, момчета, това помага много. - person Ben Granger; 08.06.2015