All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jetbrick.collection.ListOrderedMap Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/**
 * Copyright 2013-2016 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 *   Author: Guoqiang Chen
 *    Email: [email protected]
 *   WebURL: https://github.com/subchen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrick.collection;

import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

public class ListOrderedMap extends AbstractMap implements Serializable, Cloneable {
    private static final long serialVersionUID = 1L;
    private final Map> map;
    private LinkedEntry header;

    public ListOrderedMap() {
        map = new HashMap>();
        createHeader();
    }

    public ListOrderedMap(int initialCapacity) {
        map = new HashMap>(initialCapacity);
        createHeader();
    }

    public ListOrderedMap(int initialCapacity, float loadFactor) {
        map = new HashMap>(initialCapacity, loadFactor);
        createHeader();
    }

    public ListOrderedMap(Map m) {
        this();
        putAll(m);
    }

    private void createHeader() {
        header = new LinkedEntry();
        header.prev = header.next = header;
    }

    // OrderedMap interface
    public Entry getEntry(int index) {
        if (index < 0 || index >= size()) {
            throw new IndexOutOfBoundsException();
        }
        LinkedEntry entry = header.next;
        for (int i = 0; i < index; i++) {
            entry = entry.next;
        }
        return entry;
    }

    public K getKey(int index) {
        return getEntry(index).getKey();
    }

    public V getValue(int index) {
        return getEntry(index).getValue();
    }

    public Entry put(int index, K key, V value) {
        if (index < 0 || index > size()) throw new IndexOutOfBoundsException();
        LinkedEntry old = header.next;
        boolean before = false;
        for (int i = 0; i < index; i++) {
            old = old.next;
            if (key == old.key || (key != null && key.equals(old.key))) before = true;
        }
        if (before) old = old.next;
        LinkedEntry entry = map.get(key);
        if (entry != null) {
            if (index == size()) throw new IndexOutOfBoundsException();
            if (entry != old) entry.moveBefore(old);
            entry.value = value;
        } else {
            entry = new LinkedEntry(key, value);
            entry.insertBefore(old);
            map.put(key, entry);
        }
        return old;
    }

    public void putAll(int index, Map map) {
        for (Entry entry : map.entrySet())
            put(index++, entry.getKey(), entry.getValue());
    }

    public Entry remove(int index) {
        Entry entry = getEntry(index);
        remove(entry.getKey());
        return entry;
    }

    public Iterator> iterator() {
        return new EntriesIterator();
    }

    public Entry[] toArray() {
        int size = size();
        @SuppressWarnings("unchecked")
        Entry[] entries = new LinkedEntry[size];
        LinkedEntry entry = header.next;
        for (int i = 0; i < size; i++) {
            entries[i] = entry;
            entry = entry.next;
        }
        return entries;
    }

    // Map interface
    @Override
    public V get(Object key) {
        LinkedEntry entry = map.get(key);
        return entry != null ? entry.value : null;
    }

    @Override
    public V put(K key, V value) {
        LinkedEntry entry = map.get(key);
        if (entry != null) {
            V old = entry.value;
            entry.value = value;
            return old;
        } else {
            entry = new LinkedEntry(key, value);
            entry.insertBefore(header);
            map.put(key, entry);
            return null;
        }
    }

    @Override
    public V remove(Object key) {
        LinkedEntry entry = map.get(key);
        if (entry != null) {
            entry.remove();
            map.remove(key);
            return entry.value;
        } else
            return null;
    }

    @Override
    public boolean containsValue(Object value) {
        if (value != null) {
            for (V v : values())
                if (value.equals(v)) return true;
        } else {
            for (V v : values())
                if (v == null) return true;
        }
        return false;
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public void clear() {
        map.clear();
        createHeader();
    }

    private transient Set> entries;

    @Override
    public Set> entrySet() {
        if (entries == null) entries = new AbstractSet>() {

            @Override
            public int size() {
                return map.size();
            }

            @Override
            public Iterator> iterator() {
                return new EntriesIterator();
            }
        };
        return entries;
    }

    private transient Set keys;

    @Override
    public Set keySet() {
        if (keys == null) keys = new AbstractSet() {

            @Override
            public int size() {
                return map.size();
            }

            @Override
            public Iterator iterator() {
                return new KeysIterator();
            }
        };
        return keys;
    }

    private transient Collection values;

    @Override
    public Collection values() {
        if (values == null) values = new AbstractCollection() {

            @Override
            public int size() {
                return map.size();
            }

            @Override
            public Iterator iterator() {
                return new ValuesIterator();
            }
        };
        return values;
    }

    private transient List> entryList;

    public List> entryList() {
        if (entryList == null) entryList = new AbstractList>() {

            @Override
            public int size() {
                return map.size();
            }

            @Override
            public Entry get(int index) {
                return getEntry(index);
            }
        };
        return entryList;
    }

    private transient List keyList;

    public List keyList() {
        if (keyList == null) keyList = new AbstractList() {

            @Override
            public int size() {
                return map.size();
            }

            @Override
            public K get(int index) {
                return getKey(index);
            }
        };
        return keyList;
    }

    private transient List valueList;

    public List valueList() {
        if (valueList == null) valueList = new AbstractList() {

            @Override
            public int size() {
                return map.size();
            }

            @Override
            public V get(int index) {
                return getValue(index);
            }

            @Override
            public V set(int index, V element) {
                return getEntry(index).setValue(element);
            }
        };
        return valueList;
    }

    // Creates swallow clone. Keys and values are not cloned.
    @Override
    public ListOrderedMap clone() {
        return new ListOrderedMap(this);
    }

    static final class LinkedEntry extends MapEntry {
        private static final long serialVersionUID = 1L;
        private LinkedEntry prev;
        private LinkedEntry next;

        // Needed for serialization
        public LinkedEntry() {
            super(null, null);
        }

        public LinkedEntry(K key, V value) {
            super(key, value);
        }

        public void remove() {
            prev.next = next;
            next.prev = prev;
        }

        public void insertBefore(LinkedEntry entry) {
            next = entry;
            prev = entry.prev;
            entry.prev = this;
            prev.next = this;
        }

        public void moveBefore(LinkedEntry entry) {
            remove();
            insertBefore(entry);
        }

        @Override
        public String toString() {
            return value != null ? value.toString() : "null";
        }
    }

    abstract class OrderedMapIterator implements Iterator {
        private LinkedEntry curr, prev;

        public OrderedMapIterator() {
            super();
            curr = header.next;
        }

        @Override
        public boolean hasNext() {
            return curr != header;
        }

        @Override
        public void remove() {
            if (prev == null) throw new IllegalStateException();
            map.remove(prev.key);
            prev.remove();
            prev = null;
        }

        protected LinkedEntry nextEntry() {
            if (curr == header) throw new NoSuchElementException();
            prev = curr;
            curr = curr.next;
            return prev;
        }
    }

    final class EntriesIterator extends OrderedMapIterator> {
        @Override
        public Map.Entry next() {
            return nextEntry();
        }
    }

    final class KeysIterator extends OrderedMapIterator {
        @Override
        public K next() {
            return nextEntry().key;
        }
    }

    final class ValuesIterator extends OrderedMapIterator {
        @Override
        public V next() {
            return nextEntry().value;
        }
    }

    static class MapEntry implements Map.Entry, Serializable {
        private static final long serialVersionUID = 1L;
        protected K key;
        protected V value;

        /**
         * Creates new MapEntry instance with specified key and value.
         * @param key key.
         * @param value value.
         */
        public MapEntry(K key, V value) {
            super();
            this.key = key;
            this.value = value;
        }

        /**
         * @return entry key.
         */

        @Override
        public K getKey() {
            return key;
        }

        /**
         * @return entry value.
         */

        @Override
        public V getValue() {
            return value;
        }

        /**
         * Sets new value for this entry. Underlying Map should reflect the changes to this entry.
         * @param value new value.
         * @return old value.
         */

        @Override
        public V setValue(V value) {
            V old = this.value;
            this.value = value;
            return old;
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (!(o instanceof Map.Entry)) return false;
            @SuppressWarnings("unchecked")
            Map.Entry entry = (Map.Entry) o;
            K k = entry.getKey();
            if (k == key || (k != null && k.equals(key))) {
                V v = entry.getValue();
                return v == value || (v != null && v.equals(value));
            }
            return false;
        }

        @Override
        public int hashCode() {
            return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
        }

        @Override
        public String toString() {
            return key + "=" + value;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy