Home Reference Source Test

src/main/generic/utils/UniqueLinkedList.js

class UniqueLinkedList extends LinkedList {
    constructor() {
        super();
        this._map = new Map();
    }

    /**
     * @param {V|*} value
     * @returns {void}
     * @override
     */
    push(value) {
        if (!this._map.has(value)) {
            super.push(value);
        }
    }

    /**
     * @param {LinkedListEntry} entry
     * @returns {void}
     * @protected
     * @override
     */
    _push(entry) {
        super._push(entry);
        this._map.set(entry.value, entry);
    }

    /**
     * @param {V|*} value
     * @returns {void}
     */
    unshift(value) {
        if (!this._map.has(value)) {
            super.unshift(value);
        }
    }

    /**
     * @param {LinkedListEntry} entry
     * @returns {void}
     * @protected
     */
    _unshift(entry) {
        super._unshift(entry);
        this._map.set(entry.value, entry);
    }

    /**
     * @returns {V|*}
     */
    pop() {
        const value = super.pop();
        this._map.delete(value);
        return value;
    }

    /**
     * @returns {V|*}
     */
    shift() {
        const value = super.shift();
        this._map.delete(value);
        return value;
    }

    /**
     * @returns {void}
     */
    clear() {
        super.clear();
        this._map.clear();
    }

    /**
     * @param {V|*} value
     * @returns {boolean}
     */
    contains(value) {
        return this._map.has(value);
    }

    /**
     * @param {V|*} value
     * @returns {void}
     */
    remove(value) {
        const entry = this._map.get(value);
        if (entry) {
            super._remove(entry);
            this._map.delete(value);
        }
    }

    /**
     * @param {V|*} value
     * @returns {void}
     */
    moveBack(value) {
        /*
         * Just removing and inserting the key again may take seconds (yes, seconds!).
         * This is due to the JavaScript Map implementation as illustrated in this benchmark:
         * https://gist.github.com/paberr/1d916343631c0e42f8311a6f2782f30d
         *
         * 100,000 accesses using Map remove/insert: ~4s
         * 100,000 accesses using optimised version: ~9ms
         */
        const entry = this._map.get(value);
        if (entry) {
            if (entry === this._head) {
                return;
            } else if (entry === this._tail) {
                entry.next.prev = null;
                this._tail = entry.next;
            } else {
                entry.prev.next = entry.next;
                entry.next.prev = entry.prev;
            }
            entry.next = null;
            entry.prev = this._head;
            this._head.next = entry;
            this._head = entry;
        } else {
            // Do not check again for presence in the map.
            super.push(value);
        }
    }
}
Class.register(UniqueLinkedList);