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

org.magicwerk.brownies.collections.KeyListImpl Maven / Gradle / Ivy

Go to download

Brownies Collections complements the Java Collections Framework. GapList combines the strengths of both ArrayList and LinkedList. BigList is a list optimized for storing large number of elements. There are specialized List implementations for all primitive data types (IntGapList, IntBigList, IntObjGapList, IntObjBigList). The key collection classes offer support for keys and constraints for lists and collections (KeyList, KeyCollection, KeySet, Key1List, Key1Collection, Key1Set, Key2List, Key2Collection, Key2Set).

There is a newer version: 0.9.16
Show newest version
/*
 * Copyright 2012 by Thomas Mauch
 *
 * 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.
 *
 * $Id: KeyListImpl.java 3081 2016-01-05 00:29:01Z origo $
 */
package org.magicwerk.brownies.collections;
import java.util.Comparator;
import java.util.Set;

import org.magicwerk.brownies.collections.function.IFunction;
import org.magicwerk.brownies.collections.helper.Option;


/**
 * A KeyList add key handling features to GapList.
 * It is the abstract base class for both SetList and MapList.
 *
 * @author Thomas Mauch
 * @version $Id: KeyListImpl.java 3081 2016-01-05 00:29:01Z origo $
 *
 * @see GapList
 * @param  type of elements stored in the list
 */
@SuppressWarnings("serial")
public abstract class KeyListImpl extends IList {

    /**
     * Key collection used for key storage (never null).
     */
    KeyCollectionImpl keyColl;
    /**
     * List where the list content of this KeyListImpl is stored (never null).
     * If this is list sorted by element (key 0), keyColl.keyMaps[0].keysList will also reference this list.
     */
    IList list;

    /** If true the invariants are checked for debugging */
    private static final boolean DEBUG_CHECK = false;


    /**
     * Private method to check invariant of GapList.
     * It is only used for debugging.
     */
    private void debugCheck() {
    	keyColl.debugCheck();
    	//list.debugCheck();
    	assert(keyColl.keyList == this);
    	assert(list.size() == keyColl.size() || (keyColl.size() == 0 && keyColl.keyMaps == null));
    }

    /**
     * Constructor.
     */
    KeyListImpl() {
    }

    protected KeyListImpl(boolean copy, KeyListImpl that) {
        if (copy) {
        	doAssign(that);
        }
    }

    @Override
	protected void doAssign(IList that) {
		KeyListImpl list = (KeyListImpl) that;
        this.keyColl = list.keyColl;
        this.list = list.list;
	}

	@Override
    public Object clone() {
    	return copy();
    }

    @Override
    public KeyListImpl copy() {
    	@SuppressWarnings("unchecked")
		KeyListImpl copy = (KeyListImpl) super.clone();
        copy.initCopy(this);
        return copy;
    }

    /**
     * Returns a copy this list but without elements.
     * The new list will use the same comparator, ordering, etc.
     *
     * @return  an empty copy of this list
     */
    public KeyListImpl crop() {
    	@SuppressWarnings("unchecked")
		KeyListImpl crop = (KeyListImpl) super.clone();
        crop.initCrop(this);
        return crop;
    }
	/**
	 * Initialize object for crop() operation.
	 *
	 * @param that source object
	 */
    @SuppressWarnings("unchecked")
	void initCrop(KeyListImpl that) {
	    // KeyCollectionImpl
	    keyColl = new KeyCollectionImpl();
	    keyColl.initCrop(that.keyColl);
	    if (keyColl.keyList != null) {
	    	keyColl.keyList = this;
	    }

	    // List
    	if (that.keyColl.keyMaps != null && that.keyColl.keyMaps[0] != null && that.list == that.keyColl.keyMaps[0].keysList) {
    		list = (IList) keyColl.keyMaps[0].keysList;
    	} else {
    		list = that.list.doCreate(-1);
    	}

	    if (DEBUG_CHECK) debugCheck();
	}

	/**
     * Initialize object for copy() operation.
     *
     * @param that source object
     */
    @SuppressWarnings("unchecked")
	void initCopy(KeyListImpl that) {
	    // KeyCollectionImpl
	    keyColl = new KeyCollectionImpl();
	    keyColl.initCopy(that.keyColl);
	    if (keyColl.keyList != null) {
	    	keyColl.keyList = this;
	    }

        // List
	    if (that.keyColl.keyMaps != null && that.keyColl.keyMaps[0] != null && that.list == that.keyColl.keyMaps[0].keysList) {
    		list = (IList) keyColl.keyMaps[0].keysList;
    	} else {
    		list = that.list.doCreate(-1);
    		list.addAll(that.list);
    	}

        if (DEBUG_CHECK) debugCheck();
    }

    @Override
    protected void doClone(IList that) {
    }

    /**
     * Returns a Set view of the element set.
     *
     * @return set view
     * @throws IllegalArgumentException if the element set cannot be viewed as Set
     */
    public Set asSet() {
    	return new CollectionAsSet(this, false);
    }

    //-- Read

    /**
     * Determines whether this list is sorted or not.
     *
     * @return true if this a sorted list, false if not
     */
    public boolean isSorted() {
    	return keyColl.isSorted();
    }

    @Override
    public int capacity() {
   		return list.capacity();
    }

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

    @Override
    public E get(int index) {
   		return list.get(index);
    }

    @Override
    protected E doGet(int index) {
   		return list.doGet(index);
    }

    @Override
	protected  void doGetAll(T[] array, int index, int len) {
   		list.doGetAll(array, index, len);
    }

	@Override
	public boolean contains(Object elem) {
		if (keyColl.hasElemSet()) {
			return keyColl.contains(elem);
		} else {
			return super.contains(elem);
		}
	}

    //--

    /**
     * {@inheritDoc}
     * 

* Note that the behavior of the operation depends on the defined constraints. *

*/ // This method is only overwritten to change the Javadoc comment. @Override public boolean add(E elem) { return super.add(elem); } /** * Adds element if allowed and returns true. * If the element cannot be added (constraint violation like duplicated key), * false is returned. * * @param elem element to add * @return true if element has been added, false otherwise */ public boolean addIf(E elem) { try { return super.add(elem); } catch (Exception e) { return false; } } /** * {@inheritDoc} *

* Note that the behavior of the operation depends on the defined constraints. *

*/ // This method is only overwritten to change the Javadoc comment. @Override public void add(int index, E elem) { super.add(index, elem); } /** * {@inheritDoc} *

* Note that the behavior of the operation depends on the defined constraints. *

*/ // This method is only overwritten to change the Javadoc comment. @Override public E set(int index, E elem) { return super.set(index, elem); } @Override public void clear() { keyColl.clear(); list.clear(); } @Override public void ensureCapacity(int minCapacity) { // Make sure that we never allocate more slots than needed. // The add methods make sure that we never use to many slots. if (keyColl.maxSize != 0) { minCapacity = Math.min(minCapacity, keyColl.maxSize); } super.ensureCapacity(minCapacity); } @Override protected boolean doAdd(int index, E elem) { // This method is also called by doAdd(E) keyColl.checkElemAllowed(elem); // Handle maximum size and window if (keyColl.maxSize != 0 && size() >= keyColl.maxSize) { if (keyColl.movingWindow) { if (index == 0) { // the element inserted at position 0 will be removed again due to the limited size return false; } if (index == -1) { index = size(); } doRemove(0); index = index-1; } else { KeyCollectionImpl.errorMaxSize(); } } // Add element if (keyColl.isSorted()) { // Sorted list if (index == -1) { index = keyColl.binarySearchSorted(elem); if (index < 0) { index = -index-1; } } keyColl.addSorted(index, elem); // If list is sorted by element, keyColl points to list so no explicit call to its add method is needed if (!keyColl.isSortedByElem()) { list.doAdd(index, elem); } } else { // Unsorted list keyColl.addUnsorted(elem); if (index == -1) { // Element is already added to keyColl index = list.size(); } list.doAdd(index, elem); } if (DEBUG_CHECK) debugCheck(); return true; } @Override protected E doSet(int index, E elem) { keyColl.checkElemAllowed(elem); E remove = doGet(index); if (keyColl.isSorted()) { keyColl.setSorted(index, elem, remove); } else { keyColl.remove(remove); try { keyColl.add(elem); } catch (RuntimeException e) { keyColl.add(remove); throw e; } } list.doSet(index, elem); if (DEBUG_CHECK) debugCheck(); return remove; } @Override protected E doRemove(int index) { E removed = list.get(index); keyColl.remove(removed); if (!keyColl.isSortedByElem()) { list.remove(index); } if (DEBUG_CHECK) debugCheck(); return removed; } @Override protected void doRemoveAll(int index, int len) { if (keyColl.isSortedByElem()) { for (int i=0; i getKeyMapper(int keyIndex) { return keyColl.getKeyMapper(keyIndex); } /** * Returns value for given key. * If there are several values for this key, the first is returned. * If the key is not found, null is returned. * * @param keyIndex key index * @param key key to find * @return value of specified key or null */ public E getByKey(int keyIndex, Object key) { return (E) keyColl.getByKey(keyIndex, key); } /** * Returns a list with all elements with the specified key. * * @param keyIndex key index * @param key key which elements must have * @return list with all elements */ public GapList getAllByKey(int keyIndex, Object key) { return keyColl.getAllByKey(keyIndex, key); } /** * Returns number of elements with specified key. * * @param keyIndex key index * @param key key which elements must have * @return number of elements with key */ public int getCountByKey(int keyIndex, Object key) { return keyColl.getCountByKey(keyIndex, key); } /** * Removes element by key. * If there are duplicates, only one element is removed. * * @param keyIndex key index * @param key key of element to remove * @return removed element or null if no element has been removed */ protected E removeByKey(int keyIndex, Object key) { Option removed = keyColl.doRemoveByKey(keyIndex, key); if (removed.hasValue()) { int index = list.indexOf(removed.getValue()); if (index == -1) { KeyCollectionImpl.errorInvalidData(); } list.doRemove(index); } if (DEBUG_CHECK) debugCheck(); return removed.getValueOrNull(); } protected E putByKey(int keyIndex, E elem) { int index; if (keyIndex == 0 && (keyColl.keyMaps == null || keyColl.keyMaps[0] == null)) { index = indexOf(elem); } else { Object key = keyColl.getKey(keyIndex, elem); index = indexOfKey(keyIndex, key); } E replaced = null; if (index == -1) { doAdd(-1, elem); } else { replaced = doSet(index, elem); } if (DEBUG_CHECK) debugCheck(); return replaced; } /** * Removes element by key. * If there are duplicates, all elements are removed. * * @param keyIndex key index * @param key key of element to remove * @return true if elements have been removed, false otherwise */ protected GapList removeAllByKey(int keyIndex, Object key) { GapList removeds = keyColl.removeAllByKey(keyIndex, key); if (!removeds.isEmpty()) { if (!keyColl.isSortedByElem()) { if (!list.removeAll(removeds)) { KeyCollectionImpl.errorInvalidData(); } } } if (DEBUG_CHECK) debugCheck(); return removeds; } /** * Returns list containing all keys in element order. * * @param keyIndex key index * @return list containing all keys */ protected GapList getAllKeys(int keyIndex) { IFunction mapper = keyColl.getKeyMap(keyIndex).mapper; GapList list = GapList.create(); for (E obj: this) { list.add(mapper.apply(obj)); } return list; } /** * Returns set containing all distinct keys. * * @param keyIndex key index * @return list containing all distinct keys */ public Set getDistinctKeys(int keyIndex) { return keyColl.getDistinctKeys(keyIndex); } @Override public int binarySearch(int index, int len, K key, Comparator comparator) { // If this is a sorted list, it is obvious that binarySearch will work. // The list can however also be sorted without been declared as being ordered, // so we just try to do the binary search (as if Collections.binarySearch is called) return list.binarySearch(index, len, key, comparator); } @Override public void sort(int index, int len, Comparator comparator) { if (keyColl.isSorted()) { // If this is sorted list, the comparator must be equal to the specified one if (keyColl.isSortedByElem()) { if (GapList.equalsElem(comparator, keyColl.getElemSortComparator())) { return; } } throw new IllegalArgumentException("Different comparator specified for sorted list"); } else { list.sort(index, len, comparator); } } //-- Element methods @Override public IList getAll(E elem) { if (keyColl.hasElemSet()) { return getAllByKey(0, elem); } else { return list.getAll(elem); } } @Override public int getCount(E elem) { if (keyColl.hasElemSet()) { return getCountByKey(0, elem); } else { return list.getCount(elem); } } @Override public IList removeAll(E elem) { if (keyColl.hasElemSet()) { return removeAllByKey(0, elem); } else { return list.removeAll(elem); } } @SuppressWarnings("unchecked") @Override public Set getDistinct() { if (keyColl.hasElemSet()) { return (Set) getDistinctKeys(0); } else { return super.getDistinct(); } } /** * Adds or replaces element. * If there is no such element, the element is added. * If there is such an element, the element is replaced. * So said simply, it is a shortcut for the following code: *
	 * if (contains(elem)) {
	 *   remove(elem);
	 * }
	 * add(elem);
	 * 
* However the method is atomic in the sense that all or none operations are executed. * So if there is already such an element, but adding the new one fails due to a constraint violation, * the old element remains in the list. * * @param elem element * @return element which has been replaced or null otherwise */ protected E put(E elem) { int index = indexOf(elem); if (index != -1) { return set(index, elem); } else { add(elem); return null; } } /** * Invalidate element, i.e. all keys of the element are extracted * again and stored in the key maps. Old key values are removed if needed. * You must call an invalidate method if an element's key value has changed after adding it to the collection. * * @param elem element to invalidate */ protected void invalidate(E elem) { keyColl.invalidate(elem); if (keyColl.isSorted() && !keyColl.isSortedByElem()) { int oldIndex = super.indexOf(elem); int newIndex = keyColl.indexOfSorted(elem); if (oldIndex != newIndex) { list.doRemove(oldIndex); if (oldIndex < newIndex) { newIndex--; } list.doAdd(newIndex, elem); } } if (DEBUG_CHECK) debugCheck(); } /** * Invalidate key value of element. * You must call an invalidate method if an element's key value has changed after adding it to the collection. * * @param keyIndex key index * @param oldKey old key value * @param newKey new key value * @param elem element to invalidate (can be null if there are no duplicates with this key) */ protected void invalidateKey(int keyIndex, Object oldKey, Object newKey, E elem) { elem = keyColl.doInvalidateKey(keyIndex, oldKey, newKey, elem); if (keyColl.orderByKey == keyIndex && list != null) { list.doRemove(super.indexOf(elem)); int index = keyColl.indexOfSorted(elem); list.doAdd(index, elem); } if (DEBUG_CHECK) debugCheck(); } @Override protected E getDefaultElem() { return null; } @Override protected void doEnsureCapacity(int minCapacity) { list.doEnsureCapacity(minCapacity); } @Override public void trimToSize() { list.trimToSize(); } @Override protected IList doCreate(int capacity) { if (list instanceof BigList) { return new BigList(capacity); } else { return new GapList(capacity); } } //-- Key methods // The key methods can not be defined here. // Due to the generic type parameters, the methods cannot be overridden. //-- Invalidate // // public void invalidate(E elem) { // int index = indexOf(elem); // if (index == -1) { // throw new IllegalArgumentException("Element not found: " + elem); // } // invalidate(index); // } // // public void invalidate(int index) { // E elem = doGet(index); // for (int i=0; i iter = keyMap.unsortedKeys.entrySet().iterator(); // while (iter.hasNext()) { // Map.Entry entry = iter.next(); // if (equalsElem(elem, entry.getValue())) { // if (equalsElem(key, entry.getKey())) { // return null; // } // iter.remove(); // if (!allowDuplicates) { // break; // } // } // } // } else { // assert(keyMap.sortedKeys != null); // for (int i=0; i 0) { // if (index >= overlap) { // super.remove(0, overlap); // } else { // super.remove(0, index); // } // index = index - overlap; // } // if (index >= 0) { // for (int i=0; i