Package lupos.datastructures.paged_map

Source Code of lupos.datastructures.paged_map.PagedHashMultiMap

/**
* Copyright (c) 2013, Institute of Information Systems (Sven Groppe and contributors of LUPOSDATE), University of Luebeck
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
*   - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
*     disclaimer.
*   - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
*     following disclaimer in the documentation and/or other materials provided with the distribution.
*   - Neither the name of the University of Luebeck nor the names of its contributors may be used to endorse or promote
*     products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lupos.datastructures.paged_map;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;

import lupos.datastructures.buffermanager.BufferManager;
import lupos.datastructures.buffermanager.BufferManager.PageAddress;
import lupos.datastructures.buffermanager.ContinousPagesInputStream;
import lupos.datastructures.buffermanager.ContinousPagesOutputStream;
import lupos.datastructures.buffermanager.PageManager;
import lupos.datastructures.dbmergesortedds.DiskCollection;
import lupos.io.ExistingByteArrayOutputStream;
import lupos.io.Registration;
import lupos.io.helper.InputHelper;
import lupos.io.helper.LengthHelper;
import lupos.io.helper.OutHelper;
import lupos.misc.FileHelper;
import lupos.misc.Quadruple;
import lupos.misc.Triple;

/**
* PagedHashMultiMap implements a hash multi map with keys and values on disk.
*
* Multi Map means that keys are unique and multiple occurrences of elements referring to a key are counted in the map.
* Keys and elements are removed completely.
*
* It uses three files: In the first file pointers to the start position of keys are stored.
* In the second file the actual keys are stored and in the third file the actual elements are stored.
* The write access of the three files is unsynchronized.
*
* Keys or values can only be added to the keys or values file at the end.
*
* The first four bytes in the pointers file store the maximum index for the keys
* and the first 8 bytes in the keys or values file store the maximum position in the keys or values file.
* Each key and each value is part of a linked list, after the serialization of a key or value an 8-bytes pointer
* is used to point to the next key or value in the list (is 0 for marking the end of the list).
*
* see PagedHashMultiMap.pdf in folder documentation of this project
*
* @author K. Knof
*
* @param <K>
* keys
* @param <V>
* values
*/
public class PagedHashMultiMap<K,V> extends AbstractMap<K,V> {

  // the current size of keys
  long sizeKeys = 0;
  // the current size of values in all keys
  long sizeValues = 0;
  // the next free byte in the keys file
  long lastKey = 1; // the first byte is wasted, as 0 marks no reference!
  // the next free byte in the values file
  long lastValue = 1; // the first byte is wasted, as 0 marks no reference!

  // the filename of the pointers file
  final String pointersFilename;
  // the filename of the keys file
  final String keysFilename;
  // the filename of the values file
  final String valuesFilename;

  // the class of keys
  final Class<K> classOfKeys;
  // the class of values
  final Class<V> classOfValues;

  // the current id, important, if several paged hash maps are instantiated
  // (each instance gets another id and therefore stores its content into different files)
  static int fileID = 0;

  // the lock for getting a new id
  protected static ReentrantLock lock = new ReentrantLock();

  // the initial table size
  private static int INITIALTABLESIZE = 1024;

  // the current table size
  private final int TABLESIZE = INITIALTABLESIZE;

  // tablepagesize
  private final int TABLEPAGESIZE = INITIALTABLESIZE * 8;

  /**
   * @param classOfKeys
   * class of keys
   * @param classOfValues
   * class of values
   */
  public PagedHashMultiMap(final Class<K> classOfKeys, final Class<V> classOfValues) {
    this.classOfKeys = classOfKeys;
    this.classOfValues = classOfValues;
    PagedHashMultiMap.lock.lock();
    try{
      // use directory of DiskCollection for storing hash maps on disk!
      DiskCollection.makeFolders();
      final int currentID = fileID++;
      // remove old pointers, keys and values files from disk!
      final String[] dirs = DiskCollection.getTmpDir();
      for(final String dir: dirs){
        FileHelper.deleteFilesStartingWithPattern(dir, currentID + ".table_");
        FileHelper.deleteFilesStartingWithPattern(dir, currentID + ".keys_");
        FileHelper.deleteFilesStartingWithPattern(dir, currentID + ".values_");
      }
      final String dir = dirs[currentID % dirs.length];
      this.pointersFilename = dir + currentID + ".table";
      this.keysFilename = dir + currentID + ".keys";
      this.valuesFilename = dir + currentID + ".values";
    } finally {
      PagedHashMultiMap.lock.unlock();
    }
  }

  /**
   * @param classOfKeys
   * the class of keys
   * @param classOfValues
   * the class of values
   * @param pointersFilename
   * the filename of the pointers file
   * @param keysFilename
   * the filename of the keys file
   * @param valuesFilename
   * the filename of the values file
   * @param sizeKeys
   * the size of keys
   * @param sizeValues
   * the size of values in all keys
   * @param lastKey
   * the next free byte in the keys file
   * @param lastValue
   * the next free byte in the values file
   * @throws IOException
   */
  protected PagedHashMultiMap(final Class<K> classOfKeys, final Class<V> classOfValues, final String pointersFilename, final String keysFilename, final String valuesFilename, final long sizeKeys, final long sizeValues, final long lastKey, final long lastValue) throws IOException {
    this.classOfKeys = classOfKeys;
    this.classOfValues = classOfValues;
    this.pointersFilename = pointersFilename;
    this.keysFilename = keysFilename;
    this.valuesFilename = valuesFilename;
    this.sizeKeys = sizeKeys;
    this.sizeValues = sizeValues;
    this.lastKey = lastKey;
    this.lastValue = lastValue;
  }

  /**
   * read a PagedHashMultiMap from input stream
   *
   * @param lois
   * input stream
   * @return
   * PagedHashMultiMap<K,V>
   * @throws IOException
   */
  public static<K,V> PagedHashMultiMap<K,V> readLuposPagedHashMap(final InputStream lois) throws IOException{
    final String pointersFilename = InputHelper.readLuposString(lois);
    final String keysFilename = InputHelper.readLuposString(lois);
    final String valuesFilename = InputHelper.readLuposString(lois);
    @SuppressWarnings("unchecked")
    final Class<K> classOfKeys = (Class<K>) Registration.deserializeId(lois)[0];
    @SuppressWarnings("unchecked")
    final Class<V> classOfValues = (Class<V>) Registration.deserializeId(lois)[0];
    final long sizeKeys = InputHelper.readLuposLong(lois);
    final long sizeValues = InputHelper.readLuposLong(lois);
    final long lastKey = InputHelper.readLuposLong(lois);
    final long lastValue = InputHelper.readLuposLong(lois);
    return new PagedHashMultiMap<K,V>(classOfKeys, classOfValues, pointersFilename, keysFilename, valuesFilename, sizeKeys, sizeValues, lastKey, lastValue);
  }

  /**
   * write a PagedHashMultiMap to output stream
   *
   * @param loos
   * output stream
   * @throws IOException
   */
  public void writeLuposPagedHashMap(final OutputStream loos) throws IOException{
    BufferManager.getBufferManager().writeAllModifiedPages();
    OutHelper.writeLuposString(this.pointersFilename, loos);
    OutHelper.writeLuposString(this.keysFilename, loos);
    OutHelper.writeLuposString(this.valuesFilename, loos);
    Registration.serializeClass(this.classOfKeys, loos);
    Registration.serializeClass(this.classOfValues, loos);
    OutHelper.writeLuposLong(this.sizeKeys, loos);
    OutHelper.writeLuposLong(this.sizeValues, loos);
    OutHelper.writeLuposLong(this.lastKey, loos);
    OutHelper.writeLuposLong(this.lastValue, loos);
  }

  /**
   * put a key and an element referring to the key to PagedHashMultiMap
   *
   * @see java.util.AbstractMap#put(java.lang.Object, java.lang.Object)
   *
   * @param key
   * key
   * @param element
   * element referring to the key
   * @return
   * element
   */
  @Override
  public V put(final K key, final V element) {

    try {
      final PageAddress pageAddressPointers = new PageAddress(0, this.pointersFilename);
      final byte[] pagePointers = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddressPointers);

      final int hashAddress = Math.abs(key.hashCode()) % this.TABLESIZE;

      final long pointer = InputHelper.readLuposLong(new ByteArrayInputStream(pagePointers, hashAddress * 8, 8));

      final ResSet resultKey = this.containKeyGetAddressOfFoundKeyGetAddressOfLastKey(key);
      final boolean containKey = resultKey.containEntry;
      final long addressOfFoundKey = resultKey.addressOfFoundEntry;
      final long addressOfLastKey = resultKey.addressOfLastEntry;
      // check if hash table contains key
      if(!containKey) {
        this.sizeKeys++;
        final long addressOfKey = this.storeNewKey(key);

        if(pointer == 0){
          // no entry so far at this position in the hash table...
          // write address in hash table
          final OutputStream outKeys = new ExistingByteArrayOutputStream(pagePointers, hashAddress * 8);
          OutHelper.writeLuposLong(addressOfKey, outKeys);
          outKeys.close();
          BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddressPointers, pagePointers);
        } else {
          // already entries at this position in the hash table...
          this.storeAddressOfNextKey(addressOfKey, addressOfLastKey);
        }

        // store element referring to the key
        this.sizeValues++;
        final long addressOfElement = this.storeNewElement(element);
        this.storeAddressOfValues(addressOfElement, addressOfKey);
        this.storeNumberOfKeyElements(1, addressOfKey);
        return element;
      } else {
          this.sizeValues++;
          // increment number of key elements
          final long numberOfKeyElements = this.getNumberOfKeyElements(addressOfFoundKey);
          this.storeNumberOfKeyElements((numberOfKeyElements + 1), addressOfFoundKey);

          final long addressOfValues = this.getAddressOfValues(addressOfFoundKey);
          final ResSet resultElement = this.containElementGetAddressOfFoundElementGetAddressOfLastElement(element, addressOfValues);
          final boolean containElement = resultElement.containEntry;
          final long addressOfFoundElement = resultElement.addressOfFoundEntry;
          final long addressOfLastElement = resultElement.addressOfLastEntry;
          // check if list contains element
          if (containElement){
            // increment number of elements
            final long numberOfElements = this.getNumberOfElements(addressOfFoundElement);
            this.storeNumberOfElements(numberOfElements + 1, addressOfFoundElement);
            return element;
          }
          else {
            // add element to end of list
            final long addressOfElement = this.storeNewElement(element);
            this.storeAddressOfNextElement(addressOfElement, addressOfLastElement);
            return element;
          }
      }
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return element;
  }

  /**
   * this method completely removes a key from PagedHashMultiMap
   *
   * @see java.util.AbstractMap#remove(java.lang.Object)
   *
   * @param key
   * key
   * @return
   * element
   */
  @SuppressWarnings("unchecked")
  @Override
  public V remove(final Object key){
    K thiskey = null;
    try {
      thiskey = (K)key;
    } catch (final ClassCastException e){
      System.err.println("... couldn't cast object");
      System.err.println("ClassCastException: " + e.getMessage());
      return null;
    }
    final ResSet resultKey = this.containKeyGetAddressOfFoundKeyGetAddressOfLastKey(thiskey);
    final boolean containKey = resultKey.containEntry;
    final long addressOfFoundKey = resultKey.addressOfFoundEntry;
    // check if hash table contains key
    if(!containKey) {
      return null;
    } else {
      if (this.sizeKeys > 0){
        this.sizeKeys--;
      }
      final long numberOfKeyElements = this.getNumberOfKeyElements(this.getAddressOfKey(thiskey));
      if (this.sizeValues > 0) {
        this.sizeValues-=numberOfKeyElements;
      }
      long addressOfPreviousKey = 0;
      try {
        final PageAddress pageAddress = new PageAddress(0, this.pointersFilename);
        final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

        final int hashAddress = Math.abs(thiskey.hashCode()) % this.TABLESIZE;

        long pointer = InputHelper.readLuposLong(new ByteArrayInputStream(page, hashAddress * 8, 8));

        // check existing list of entries for this table position
        Quadruple<K, Long, Long, Long> entry;
        boolean found = false;
        do {
          entry = this.getKey(pointer);
          if(entry.getFourth() == addressOfFoundKey) {
            // found!
            addressOfPreviousKey = pointer;
            found = true;
          }
          if((entry.getFourth() == 0)) {
            found = true;
          }
          // go to next entry in list...
          pointer = entry.getFourth();
        } while(!found);
      } catch (final IOException e) {
        System.err.println(e);
        e.printStackTrace();
      }
      // check if key is first entry in key list
      if (addressOfPreviousKey == 0) {
        // check if key is last entry in key list
        if (this.getKey(addressOfFoundKey).getFourth() == 0){
          try {
            final PageAddress pageAddress = new PageAddress(0, this.pointersFilename);
            final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

            final int hashAddress = Math.abs(thiskey.hashCode()) % this.TABLESIZE;

            final OutputStream outKeys = new ExistingByteArrayOutputStream(page, hashAddress * 8);
            OutHelper.writeLuposLong(0, outKeys);
            outKeys.close();
            BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
          } catch (final IOException e) {
            System.err.println(e);
            e.printStackTrace();
          }
        } // key is not last entry in key list
        else {
          try {
            final PageAddress pageAddressPointers = new PageAddress(0, this.pointersFilename);
            final byte[] pagePointers = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddressPointers);

            final int hashAddress = Math.abs(thiskey.hashCode()) % this.TABLESIZE;

            final OutputStream outKeys = new ExistingByteArrayOutputStream(pagePointers, hashAddress * 8);
            OutHelper.writeLuposLong(this.getKey(addressOfFoundKey).getFourth(), outKeys);
            outKeys.close();
            BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddressPointers, pagePointers);
          } catch (final IOException e) {
            System.err.println(e);
            e.printStackTrace();
          }
        }
      } // key is not first entry in key list
      else {
        // check if key is last entry in key list
        if (this.getKey(addressOfFoundKey).getFourth() == 0){
          this.storeAddressOfNextKey(0, addressOfPreviousKey);
        } // key is not last entry in key list
        else {
          this.storeAddressOfNextKey(this.getKey(addressOfFoundKey).getFourth(), addressOfPreviousKey);
        }
      }
    return null;
    }
  }

  /**
   * this method completely removes an element referring to a key from PagedHashMultiMap
   *
   * @param key
   * key
   * @param element
   * element
   * @return
   * element
   */
  public V removeKeyWithValue(final K key, final V element){
    final ResSet resultKey = this.containKeyGetAddressOfFoundKeyGetAddressOfLastKey(key);
    final boolean containKey = resultKey.containEntry;
    final long addressOfFoundKey = resultKey.addressOfFoundEntry;
    // check if hash table contains key
    if(!containKey) {
      return null;
    } else {

      final long addressOfValues = this.getAddressOfValues(addressOfFoundKey);
      final ResSet resultElement = this.containElementGetAddressOfFoundElementGetAddressOfLastElement(element, addressOfValues);
      final boolean containElement = resultElement.containEntry;
      final long addressOfFoundElement = resultElement.addressOfFoundEntry;
      // check if hash table contains element referring to key
      if(!containElement){
        return null;
      } else {


        final long numberOfElements = this.getNumberOfElements(addressOfFoundElement);
        if (this.sizeValues > 0){
          this.sizeValues -= numberOfElements;
        }
        final long numberOfKeyElements = this.getNumberOfKeyElements(addressOfFoundKey);
        final long newNumberOfKeyElements = numberOfKeyElements - numberOfElements;
        this.storeNumberOfKeyElements(newNumberOfKeyElements, addressOfFoundKey);
        if (newNumberOfKeyElements == 0){
          this.remove(key);
          return null;
        } else {
          long addressOfPreviousElement = 0;
          long pointer = addressOfValues;
          // check existing list of elements
          Triple<V, Long, Long> entry;
          boolean found = false;
          do {
            entry = this.getElement(pointer);
            if(entry.getThird() == addressOfFoundElement) {
              // found!
              addressOfPreviousElement = pointer;
              found = true;
            }
            if((entry.getThird() == 0)) {
              found = true;
            }
            // go to next entry in list...
            pointer = entry.getThird();
          } while(!found);
          // check if element is first entry in element list
          if (addressOfPreviousElement == 0) {
            // check if element is last entry in element list
            if (this.getElement(addressOfFoundElement).getThird() == 0){
              // if element is the first and last element in list: remove key
              this.remove(key);
            } else {
              // element is not last entry in element list
              this.storeAddressOfValues(this.getElement(addressOfFoundElement).getThird(), addressOfFoundKey);
            }
          } // element is not first entry in element list
          else {
            // check if element is last entry in element list
            if (this.getElement(addressOfFoundElement).getThird() == 0){
              this.storeAddressOfNextElement(0, addressOfPreviousElement);
            } // element is not last entry in element list
            else {
              this.storeAddressOfNextElement(this.getElement(addressOfFoundElement).getThird(), addressOfPreviousElement);
            }
          }
          return null;
        }
      }
    }
  }

  /**
   * this method removes all duplicate of elements
   *
   * @param key
   * key
   * @param element
   * element
   * @return
   * remove status
   */
  public boolean removeAllDuplicates(final K key, final V element) {
    final ResSet resultKey = this.containKeyGetAddressOfFoundKeyGetAddressOfLastKey(key);
    final boolean containKey = resultKey.containEntry;
    final long addressOfFoundKey = resultKey.addressOfFoundEntry;
    // check if hash table contains key
    if(!containKey) {
      return false;
    } else {
      // check existing list of entries
      if(this.getNumberOfKeyElements(addressOfFoundKey)> 0){
        final long addressOfValues = this.getAddressOfValues(addressOfFoundKey);
        final ResSet resultElement = this.containElementGetAddressOfFoundElementGetAddressOfLastElement(element, addressOfValues);
        final boolean containElement = resultElement.containEntry;
        final long addressOfFoundElement = resultElement.addressOfFoundEntry;
        if (containElement){
          final long numberOfElements = this.getNumberOfElements(addressOfFoundElement);
          if (numberOfElements > 0) {
            // set number of elements to zero and decrement number of key elements by number of elements
            this.setNumberOfElementsTo0(addressOfFoundElement);
            this.sizeValues-=numberOfElements;
            final long numberOfKeyElements = this.getNumberOfKeyElements(addressOfFoundKey);
            this.storeNumberOfKeyElements(numberOfKeyElements - numberOfElements, addressOfFoundKey);
            return true;
          } else {
            return false;
          }
        } else {
          return false;
        }
      } else {
        return false;
      }
    }
  }

  /**
   * set of entries without duplicates
   *
   * @see java.util.AbstractMap#entrySet()
   */
  @Override
  public Set<java.util.Map.Entry<K, V>> entrySet() {
    final Set<Entry<K, V>> mapSet = new HashSet<Entry<K, V>>();
    SimpleEntry<K,V> entry = null;
    final Iterator<K> itkeys = this.iteratorKeys();
    while(itkeys.hasNext()){
      final K key = itkeys.next();
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        final Iterator<V> itelements = this.iteratorElements(key);
        while(itelements.hasNext()){
          final V element = itelements.next();
          entry = new SimpleEntry<K,V>(key, element);
          mapSet.add(entry);
        }
      }
    }
    return mapSet;
  }

  /**
   * list of entries with duplicates
   *
   * @return
   * list of entries
   */
  public List<java.util.Map.Entry<K, V>> entryList() {
    final List<Entry<K, V>> mapSet = new ArrayList<Entry<K, V>>();
    SimpleEntry<K,V> entry = null;
    final Iterator<K> itkeys = this.iteratorKeys();
    while(itkeys.hasNext()){
      final K key = itkeys.next();
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        final Iterator<V> itelements = this.iteratorWithDuplicates(key);
        while(itelements.hasNext()){
          final V element = itelements.next();
          entry = new SimpleEntry<K,V>(key, element);
          mapSet.add(entry);
        }
      }
    }
    return mapSet;
  }

  /* (non-Javadoc)
   * @see java.util.AbstractMap#keySet()
   */
  @Override
  public Set<K> keySet() {
    final Set<K> keySet = new HashSet<K>();
    final Iterator<K> itkeys = this.iteratorKeys();
    while(itkeys.hasNext()){
      final K key = itkeys.next();
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        keySet.add(key);
      }
    }
    return keySet;
  }

  /**
   * values list with duplicates
   *
   * @param key
   * key
   * @return
   * values list
   */
  public Collection<V> getValuesList(final K key){
    final List<V> valuesList = new ArrayList<V>();
    if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
      final Iterator<V> itelements = this.iteratorWithDuplicates(key);
      while(itelements.hasNext()){
        final V element = itelements.next();
        valuesList.add(element);
      }
    }
    return valuesList;
  }

  /**
   * collection of element with duplicates
   *
   * @param key
   * key
   * @return
   * collection of elements
   * @throws IOException
   */
  public Collection<V> getCollection(final K key) throws IOException{

    if (this.getAddressOfKey(key) == 0){
      return null;
    } else {
      final GetCollection<K, V> coll = new GetCollection<K, V>(this.classOfKeys, this.classOfValues, this.pointersFilename, this.keysFilename, this.valuesFilename, this.sizeKeys, this.sizeValues, this.lastKey, this.lastValue, key, fileID);
      return coll.getCollection();
    }
  }

  /**
   * collection of values with duplicates
   *
   * @see java.util.AbstractMap#values()
   */
  @Override
  public Collection<V> values(){
    final List<V> values = new ArrayList<V>();
    final Iterator<K> itkeys = this.iteratorKeys();
    while(itkeys.hasNext()){
      final K key = itkeys.next();
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        final Iterator<V> itelements = this.iteratorWithDuplicates(key);
        while(itelements.hasNext()){
          final V element = itelements.next();
          values.add(element);
        }
      }
    }
    return values;
  }

  /**
   * this method tests if PagedHashMultiMap contains key
   *
   * @param key
   * key
   * @return
   * result set contain key, address of found key, addres of last key
   */
  public ResSet containKeyGetAddressOfFoundKeyGetAddressOfLastKey(final K key) {
    try {
      final PageAddress pageAddress = new PageAddress(0, this.pointersFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final int hashAddress = Math.abs(key.hashCode()) % this.TABLESIZE;

      long pointer = InputHelper.readLuposLong(new ByteArrayInputStream(page, hashAddress * 8, 8));

      if(pointer == 0) {
        // no entry so far at this position in the hash table...
        return new ResSet (false, 0, 0);
      } else {
        // check existing list of entries for this table position
        Quadruple<K, Long, Long, Long> entry;
        do {
          entry = this.getKey(pointer);
          if(entry.getFirst().equals(key)) {
            // found!
            return new ResSet (true, pointer, 0);
          }
          if(entry.getFourth() == 0) {
            // end of this list reached!
            return new ResSet (false, 0, pointer);
          }
          // go to next entry in list...
          pointer = entry.getFourth();
        } while(true);
      }
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return new ResSet (false, 0, 0);
    }

  /**
   * this method tests if PagedHashMultiMap contains an element referring to a key
   *
   * @param element
   * element
   * @param addressOfValues
   * address of values list of key
   * @return
   * result set contain element, address of found element, address of last element
   */
  public ResSet containElementGetAddressOfFoundElementGetAddressOfLastElement(final V element, final long addressOfValues) {
    long pointer = addressOfValues;
    if(pointer == 0) {
      // no entry so far...
      return new ResSet(false, 0, 0);
    } else {
      // check existing list of elements
      Triple<V, Long, Long> entry;
      do {
        entry = this.getElement(pointer);
        if(entry.getFirst().equals(element)) {
          // found!
          return new ResSet(true, pointer, 0);
        }
        if(entry.getThird() == 0) {
          // end of this list reached!
          return new ResSet(false, 0, pointer);
        }
        // go to next entry in list...
        pointer = entry.getThird();
      } while(true);
    }
    }

  /**
   * this method stores a new key
   *
   * @param key
   * key
   * @return
   * address of key
   */
  private long storeNewKey(final K key) {

    if (!(key == null)){
      final long iLastKey = this.lastKey;

      this.storeKey(key, 0, 0, 0, this.lastKey);

      this.lastKey += Registration.lengthSerializeWithoutId(key) + 3 * LengthHelper.lengthLuposLong();

      return iLastKey;
    }
    return this.lastKey;
  }

  /**
   * this method stores a new element
   *
   * @param element
   * element
   * @return
   * address of element
   */
  private long storeNewElement(final V element) {

    if (!(element == null)){
      final long iLastValue = this.lastValue;

      this.storeElement(element, 1, 0, this.lastValue);

      this.lastValue += Registration.lengthSerializeWithoutId(element) + 2 * LengthHelper.lengthLuposLong();

      return iLastValue;
    }
    return this.lastValue;
  }

  /**
   * this method stores a key
   *
   * @param key
   * key
   * @param addressOfValues
   * address of values
   * @param numberOfKeyElements
   * number of key elements
   * @param addressOfNextKey
   * address of next key
   * @param addressKey
   * address of key
   */
  private final void storeKey(final K key, final long addressOfValues, final long numberOfKeyElements, final long addressOfNextKey, final long addressKey) {
    final int pagenumber = (int) (addressKey / PageManager.getDefaultPageSize());
    final int index = (int) (addressKey % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.keysFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      OutHelper.writeLuposLong(addressOfValues, out);
      OutHelper.writeLuposLong(numberOfKeyElements, out);
      OutHelper.writeLuposLong(addressOfNextKey, out);
      Registration.serializeWithoutId(key, out);
      out.close();
      BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
  }

  /**
   * this method stores an element
   *
   * @param element
   * element
   * @param numberOfElements
   * number of elements
   * @param addressOfNextElement
   * address of next element
   * @param addressElement
   * address element
   */
  private final void storeElement(final V element, final long numberOfElements, final long addressOfNextElement, final long addressElement) {
    final int pagenumber = (int) (addressElement / PageManager.getDefaultPageSize());
    final int index = (int) (addressElement % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.valuesFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
      OutHelper.writeLuposLong(numberOfElements, out);
      OutHelper.writeLuposLong(addressOfNextElement, out);
      Registration.serializeWithoutId(element, out);
      out.close();
      BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
  }

  /**
   * this method stores the address of values
   *
   * @param addressOfValues
   * address of values
   * @param addressKey
   * address of key
   */
  public final void storeAddressOfValues(final long addressOfValues, final long addressKey){
    final int pagenumber = (int) (addressKey / PageManager.getDefaultPageSize());
    final int index = (int) (addressKey % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.keysFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      OutHelper.writeLuposLong(addressOfValues, out);
      out.close();
      BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
  }

  /**
   * this method stores the number of key elements
   *
   * @param numberOfKeyElements
   * number of kez elements
   * @param address
   * address
   */
  private final void storeNumberOfKeyElements(final long numberOfKeyElements, final long address){
    this.storeAddressOfValues(numberOfKeyElements, address + 8);
  }

  /**
   * this method stores the address of next key
   *
   * @param addressOfNextEntry
   * address of next key
   * @param address
   * address
   */
  private final void storeAddressOfNextKey(final long addressOfNextEntry, final long address){
    this.storeNumberOfKeyElements(addressOfNextEntry, address + 8);
  }

  /**
   * this method stores the number of elements
   *
   * @param numberOfElements
   * number of elements
   * @param addressElement
   * address of element
   */
  private final void storeNumberOfElements(final long numberOfElements, final long addressElement){
    final int pagenumber = (int) (addressElement / PageManager.getDefaultPageSize());
    final int index = (int) (addressElement % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.valuesFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
      OutHelper.writeLuposLong(numberOfElements, out);
      out.close();
      BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
  }

  /**
   * this method stores the address of next element
   *
   * @param addressOfNextEntry
   * address of next element
   * @param address
   * address
   */
  public final void storeAddressOfNextElement(final long addressOfNextEntry, final long address){
    this.storeNumberOfElements(addressOfNextEntry, address + 8);
  }

  /**
   * structure of data
   *
   * @param address
   * address of key
   * @return
   * Quadruple of key informations
   */
  public final Quadruple<K, Long, Long, Long> getKey(final long address) {
    final int pagenumber = (int) (address / PageManager.getDefaultPageSize());
    final int index = (int) (address % PageManager.getDefaultPageSize());
    try {
      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      final long addressOfValues = InputHelper.readLuposLong(in);
      final long numberOfKeyElements = InputHelper.readLuposLong(in);
      final long addressOfNextKey = InputHelper.readLuposLong(in);
      final K key = Registration.deserializeWithoutId(this.classOfKeys, in);
      in.close();
      return new Quadruple<K, Long, Long, Long>(key, addressOfValues, numberOfKeyElements, addressOfNextKey);
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    } catch (final ClassNotFoundException e) {
      System.err.println(e);
      e.printStackTrace();
    } catch (final URISyntaxException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return null;
  }


/**
* structure of data
*
* @param address
* address of element
* @return
* Triple of element informations
*/
private final Triple<V, Long, Long> getElement(final long address) {
    final int pagenumber = (int) (address / PageManager.getDefaultPageSize());
    final int index = (int) (address % PageManager.getDefaultPageSize());
    try {
      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
      final long numberOfElements = InputHelper.readLuposLong(in);
      final long addressOfNextElement = InputHelper.readLuposLong(in);
      final V element = Registration.deserializeWithoutId(this.classOfValues, in);
      in.close();
      return new Triple<V, Long, Long>(element, numberOfElements, addressOfNextElement);
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    } catch (final ClassNotFoundException e) {
      System.err.println(e);
      e.printStackTrace();
    } catch (final URISyntaxException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return null;
  }

  /**
   * this method decrements the number of key elements
   *
   * @param addressKey
   * address of key
   * @return
   * decrement status
   */
  private final boolean decrementNumberOfKeyElements(final long addressKey){
    final int pagenumber = (int) (addressKey / PageManager.getDefaultPageSize());
    final int index = (int) (addressKey % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.keysFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      final long addressOfValues = InputHelper.readLuposLong(in);
      long numberOfKeyElements = InputHelper.readLuposLong(in);
      in.close();
      if(numberOfKeyElements>0){
        numberOfKeyElements--;
        final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
        OutHelper.writeLuposLong(addressOfValues, out);
        OutHelper.writeLuposLong(numberOfKeyElements, out);
        out.close();
        BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
        return true;
      }
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return false;
  }

  /**
   * this method decrements the number of elements
   *
   * @param addressElement
   * address of element
   * @return
   * decrement status
   */
  private final boolean decrementNumberOfElements(final long addressElement){
    final int pagenumber = (int) (addressElement / PageManager.getDefaultPageSize());
    final int index = (int) (addressElement % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.valuesFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
      long numberOfElements = InputHelper.readLuposLong(in);
      in.close();
      if(numberOfElements>0){
        numberOfElements--;
        final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
        OutHelper.writeLuposLong(numberOfElements, out);
        out.close();
        BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
        return true;
      }
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return false;
  }

  /**
   * this method sets the number of key elements to 0
   *
   * @param addressKey
   * address of key
   * @return
   * address
   */
  public final long setNumberOfKeyElementsTo0(final long addressKey){
    final int pagenumber = (int) (addressKey / PageManager.getDefaultPageSize());
    final int index = (int) (addressKey % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.keysFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      final long addressOfValues = InputHelper.readLuposLong(in);
      final long numberOfKeyElements = InputHelper.readLuposLong(in);
      in.close();
      if(numberOfKeyElements>0){
        final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
        OutHelper.writeLuposLong(addressOfValues, out);
        OutHelper.writeLuposLong(0, out);
        out.close();
        BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
        return numberOfKeyElements;
      }
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return 0;
  }

  /**
   * this method sets the number of elements to 0
   *
   * @param addressElement
   * address of element
   * @return
   * address
   */
  public final long setNumberOfElementsTo0(final long addressElement){
    final int pagenumber = (int) (addressElement / PageManager.getDefaultPageSize());
    final int index = (int) (addressElement % PageManager.getDefaultPageSize());
    try {
      final PageAddress pageAddress = new PageAddress(0, this.valuesFilename);
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);

      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
      final long numberOfElements = InputHelper.readLuposLong(in);
      in.close();
      if(numberOfElements>0){
        final OutputStream out = new ContinousPagesOutputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
        OutHelper.writeLuposLong(0, out);
        out.close();
        BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
        return numberOfElements;
      }
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return 0;
  }

  /**
   * iterator with duplicates of elements referring to the key
   *
   * @param key
   * key
   * @return
   * Iterator
   */
  public Iterator<V> iteratorWithDuplicates(final K key) {

    return new Iterator<V>(){

      private long current = 0;

      private long addressOfNextEntry = 0;

      private long addressOfCurrentEntry = 0;

      private V currentElement = null;

      private long remainingNumberOfElements = 0;

      private final long addressOfKey = PagedHashMultiMap.this.getAddressOfKey(key);

      private final long numberOfKeyElements = PagedHashMultiMap.this.getNumberOfKeyElements(this.addressOfKey);

      long addressOfValues = PagedHashMultiMap.this.getAddressOfValues(this.addressOfKey);

      /* (non-Javadoc)
       * @see java.util.Iterator#hasNext()
       */
      @Override
      public boolean hasNext() {

        return (this.current < this.numberOfKeyElements);
      }

      /* (non-Javadoc)
       * @see java.util.Iterator#next()
       */
      @Override
      public V next() {
        if(this.hasNext()){
          if(this.remainingNumberOfElements>0){
            this.current++;
            this.remainingNumberOfElements--;
            return this.currentElement;
          }

          if(this.addressOfNextEntry == 0){
            this.addressOfNextEntry = this.addressOfValues;
          }

          this.addressOfCurrentEntry = this.addressOfNextEntry;
          final Triple<V, Long, Long> entry = PagedHashMultiMap.this.getElement(this.addressOfNextEntry);

          this.currentElement = entry.getFirst();
          this.remainingNumberOfElements = entry.getSecond();
          this.addressOfNextEntry = entry.getThird();

          return this.next();
        }
        return null;
      }

      /* (non-Javadoc)
       * @see java.util.Iterator#remove()
       */
      @Override
      public void remove() {
        if(this.addressOfCurrentEntry>0){
          if(PagedHashMultiMap.this.decrementNumberOfElements(this.addressOfCurrentEntry)){
            PagedHashMultiMap.this.sizeValues--;
            PagedHashMultiMap.this.decrementNumberOfKeyElements(this.addressOfKey);
            this.current--;
          }
        }
      }
    };
  }

  /**
   * iterator of keys
   *
   * @return
   * Iterator
   */
  public Iterator<K> iteratorKeys() {

    return new Iterator<K>(){

      private long current = 0;

      private int address = -8;

      private long addressOfNextEntry = 0;

      private long addressOfCurrentEntry = 0;

      private K currentKey = null;

      private long remainingNumberOfKeys = 0;

      /* (non-Javadoc)
       * @see java.util.Iterator#hasNext()
       */
      @Override
      public boolean hasNext() {
        return (this.current < PagedHashMultiMap.this.sizeKeys);
      }

      /* (non-Javadoc)
       * @see java.util.Iterator#next()
       */
      @Override
      public K next() {
        if(this.hasNext()){
          if(this.remainingNumberOfKeys>0){
            this.current++;
            this.remainingNumberOfKeys--;
            return this.currentKey;
          }

          if(this.addressOfNextEntry == 0){

            try {
              final PageAddress pageAddress = new PageAddress(0, PagedHashMultiMap.this.pointersFilename);
              final byte[] page = BufferManager.getBufferManager().getPage(PagedHashMultiMap.this.TABLEPAGESIZE, pageAddress);

              long pointer;
              do {
                this.address += 8;
                pointer = InputHelper.readLuposLong(new ByteArrayInputStream(page, this.address, 8));
              } while(pointer==0);
              this.addressOfNextEntry = pointer;
            } catch (final IOException e) {
              System.err.println(e);
              e.printStackTrace();
            }
          }

          this.addressOfCurrentEntry = this.addressOfNextEntry;
          final Quadruple<K, Long, Long, Long> entry = PagedHashMultiMap.this.getKey(this.addressOfNextEntry);

          this.currentKey = entry.getFirst();
          this.remainingNumberOfKeys = 1;
          this.addressOfNextEntry = entry.getFourth();

          return this.next();
        }
        return null;
      }

      /* (non-Javadoc)
       * @see java.util.Iterator#remove()
       */
      @Override
      public void remove() {
        if(this.addressOfCurrentEntry>0){
          final long diff = PagedHashMultiMap.this.setNumberOfKeyElementsTo0(this.addressOfCurrentEntry);
          if(diff>0){
            PagedHashMultiMap.this.sizeValues-=diff;
            this.current-=diff;
          }
        }
      }
    };
  }

  /**
   * iterator without duplicates of elements referring to a key
   *
   * @param key
   * key
   * @return
   * Iterator
   */
  public Iterator<V> iteratorElements(final K key) {

    return new Iterator<V>(){

      private long current = 0;

      private long addressOfNextEntry = 0;

      private long addressOfCurrentEntry = 0;

      private V currentElement = null;

      private long remainingNumberOfElements = 0;

      private final long addressOfKey = PagedHashMultiMap.this.getAddressOfKey(key);
      private final long numberOfKeyElements = PagedHashMultiMap.this.getNumberOfKeyElements(this.addressOfKey);

      /* (non-Javadoc)
       * @see java.util.Iterator#hasNext()
       */
      @Override
      public boolean hasNext() {
        return (this.current < this.numberOfKeyElements);
      }

      /* (non-Javadoc)
       * @see java.util.Iterator#next()
       */
      @Override
      public V next() {
        if(this.hasNext()){
          if(this.remainingNumberOfElements>0){
            this.current+=this.remainingNumberOfElements;
            this.remainingNumberOfElements=0;
            return this.currentElement;
          }

          if(this.addressOfNextEntry == 0){
            final long addressOfValues = PagedHashMultiMap.this.getAddressOfValues(this.addressOfKey);
            this.addressOfNextEntry = addressOfValues;
          }

          this.addressOfCurrentEntry = this.addressOfNextEntry;
          final Triple<V, Long, Long> entry = PagedHashMultiMap.this.getElement(this.addressOfNextEntry);

          this.currentElement = entry.getFirst();
          this.remainingNumberOfElements = entry.getSecond();
          this.addressOfNextEntry = entry.getThird();

          return this.next();
        }
        return null;
      }

      /* (non-Javadoc)
       * @see java.util.Iterator#remove()
       */
      @Override
      public void remove() { // remove all entries inclusive duplicates!
        if(this.addressOfCurrentEntry>0){
          final long diff = PagedHashMultiMap.this.setNumberOfElementsTo0(this.addressOfCurrentEntry);
          if(diff>0){
            PagedHashMultiMap.this.sizeValues-=diff;
            final long numberOfKeyElements = PagedHashMultiMap.this.getNumberOfKeyElements(this.addressOfKey);
            PagedHashMultiMap.this.storeNumberOfKeyElements(numberOfKeyElements - diff, this.addressOfKey);
            this.current-=diff;
          }
        }
      }
    };
  }

  /**
   * @param key
   * key
   * @return
   * address of key
   */
  public long getAddressOfKey(final K key){
    final ResSet resultKey = this.containKeyGetAddressOfFoundKeyGetAddressOfLastKey(key);
    final boolean containKey = resultKey.containEntry;
    final long addressOfFoundKey = resultKey.addressOfFoundEntry;
    if (containKey){
      return addressOfFoundKey;
    } else {
      return 0;
    }
  }

  /**
   * @param addressKey
   * address of key
   * @return
   * address of values
   */
  public long getAddressOfValues(final long addressKey){
    final int pagenumber = (int) (addressKey / PageManager.getDefaultPageSize());
    final int index = (int) (addressKey % PageManager.getDefaultPageSize());
    try {
      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      final long addressOfValues = InputHelper.readLuposLong(in);
      in.close();
      return addressOfValues;
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return 0;
  }

  /**
   * @param addressKey
   * address of key
   * @return
   * number of key elements
   */
  public long getNumberOfKeyElements(final long addressKey){
    final int pagenumber = (int) (addressKey / PageManager.getDefaultPageSize());
    final int index = (int) (addressKey % PageManager.getDefaultPageSize());
    try {
      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      @SuppressWarnings("unused")
      final long addressOfValues = InputHelper.readLuposLong(in);
      final long numberOfKeyElements = InputHelper.readLuposLong(in);
      in.close();
      return numberOfKeyElements;
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return 0;
  }

  /**
   * @param address
   * address of key
   * @return
   * address of next key
   */
  @SuppressWarnings("unused")
  private long getAddressOfNextKey(final long address){
    final int pagenumber = (int) (address / PageManager.getDefaultPageSize());
    final int index = (int) (address % PageManager.getDefaultPageSize());
    try {
      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.keysFilename, false, false), index);
      final long addressOfValues = InputHelper.readLuposLong(in);
      final long numberOfKeyElements = InputHelper.readLuposLong(in);
      final long addressOfNextKey = InputHelper.readLuposLong(in);
      in.close();
      return addressOfNextKey;
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return 0;
  }

  /**
   * @param key
   * key
   * @param element
   * element
   * @return
   * address of element
   */
  public long getAddressOfElement(final K key, final V element){
    final ResSet resultKey = this.containKeyGetAddressOfFoundKeyGetAddressOfLastKey(key);
    final boolean containKey = resultKey.containEntry;
    final long addressOfFoundKey = resultKey.addressOfFoundEntry;
    if (!containKey){
      return 0;
    } else {
      final long addressOfValues = this.getAddressOfValues(addressOfFoundKey);
      final ResSet result = this.containElementGetAddressOfFoundElementGetAddressOfLastElement(element, addressOfValues);
      final boolean containElement = result.containEntry;
      final long addressOfElement = result.addressOfFoundEntry;
      if (containElement){
        return addressOfElement;
      } else {
        return 0;
      }
    }
  }

  /**
   * @param addressElement
   * addres of element
   * @return
   * number of elements
   */
  private long getNumberOfElements(final long addressElement){
    final int pagenumber = (int) (addressElement / PageManager.getDefaultPageSize());
    final int index = (int) (addressElement % PageManager.getDefaultPageSize());
    try {
      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
      final long numberOfElements = InputHelper.readLuposLong(in);
      in.close();
      return numberOfElements;
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return 0;
  }

  /**
   * @param address
   * address of element
   * @return
   * address of next element
   */
  @SuppressWarnings("unused")
  private long getAddressOfNextElement(final long address){
    final int pagenumber = (int) (address / PageManager.getDefaultPageSize());
    final int index = (int) (address % PageManager.getDefaultPageSize());
    try {
      final InputStream in = new ContinousPagesInputStream(pagenumber, new PageManager(this.valuesFilename, false, false), index);
      final long numberOfElements = InputHelper.readLuposLong(in);
      final long addressOfNextElement = InputHelper.readLuposLong(in);
      in.close();
      return addressOfNextElement;
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    return 0;
  }

  // with duplicates
  /* (non-Javadoc)
   * @see java.util.AbstractMap#size()
   */
  @Override
  public int size() {
    return (int) this.sizeValues;
  }

  /* (non-Javadoc)
   * @see java.util.AbstractMap#isEmpty()
   */
  @Override
  public boolean isEmpty() {
    if (this.sizeValues == 0) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * @return
   * file ID
   */
  public int getFileID() {
    return PagedHashMultiMap.fileID;
  }

  /**
   * this method sts the file ID
   *
   * @param fileID
   * file ID
   */
  public void setFileID(final int fileID) {
    PagedHashMultiMap.fileID = fileID;
  }

  /* (non-Javadoc)
   * @see java.util.AbstractMap#clear()
   */
  @Override
  public void clear() {
    final PageAddress pageAddress = new PageAddress(0, this.pointersFilename);
    try {
      // delete content of pointers page...
      final byte[] page = BufferManager.getBufferManager().getPage(this.TABLEPAGESIZE, pageAddress);
      for(int i=0; i<page.length; i++){
        page[i]=0;
      }
      BufferManager.getBufferManager().modifyPage(this.TABLEPAGESIZE, pageAddress, page);
    } catch (final IOException e) {
      System.err.println(e);
      e.printStackTrace();
    }
    this.sizeKeys = 0;
    this.sizeValues = 0;
    this.lastKey = 0;
    this.lastValue = 0;
  }

  /**
   * this method releases all
   *
   * @throws IOException
   */
  public void release() throws IOException {
    BufferManager.getBufferManager().releaseAllPages(this.pointersFilename);
    BufferManager.getBufferManager().close(this.pointersFilename);
  }

  /**
   * String presentation without duplicates of PagedHashMultiMap
   *
   * @return
   * String of map
   */
  public String toStringWithoutDuplicates() {
    String result = "Paged Hash Map: { ";
    final Iterator<K> itkeys = this.iteratorKeys();
    while(itkeys.hasNext()){
      final K key = itkeys.next();
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        result+=key;
        result+=" = [ ";
      }
      boolean firstTimeElements = true;
      final Iterator<V> itelements = this.iteratorElements(key);
      while(itelements.hasNext()){
        final V entry = itelements.next();
        if(firstTimeElements){
          firstTimeElements = false;
        } else {
          result+=", ";
        }
        result+=entry;
      }
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        result+=" ] ";
      }
    }
    return result+" }";
  }

  /* (non-Javadoc)
   * @see java.util.AbstractMap#toString()
   */
  @ Override
  public String toString(){
    String result = "Paged Hash Map: { ";
    final Iterator<K> itkeys = this.iteratorKeys();
    while(itkeys.hasNext()){
      final K key = itkeys.next();
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        result+=key;
        result+=" = [ ";
      }
      boolean firstTimeElements = true;
      final Iterator<V> itelements = this.iteratorWithDuplicates(key);
      while(itelements.hasNext()){
        final V entry = itelements.next();
        if(firstTimeElements){
          firstTimeElements = false;
        } else {
          result+=", ";
        }
        result+=entry;
      }
      if (!(this.getKey(this.getAddressOfKey(key)).getThird() == 0)){
        result+=" ] ";
      }
    }
    return result+" }";
  }

  /**
   * this method tests the implementation
   *
   * @param args
   * console arguments
   */
  public static void main(final String[] args) throws IOException {
    final PagedHashMultiMap<String, String> m = new PagedHashMultiMap<String, String>(String.class, String.class);
    System.out.println("is empty: " + m.isEmpty());
    m.put("alfons", "Alf");
    m.put("alfons", "A");
    m.put("alfons", "Alf");
    m.put("boris", "Bertha");
    m.put("boris", "Bertha");
    m.put("boris", "Brit");
    m.put("boris", "Beate");
    m.put("c", "C");
    m.put("d", "D");
    m.put("f", "F");
    m.put("f", "Fred");
    m.put("f", "Ferdi");
    m.put("f", "Fill");
    m.put("f", "Fred");
    System.out.println(m);
    System.out.println("size: " + m.size());
    System.out.println("file ID: " + m.getFileID());
    System.out.println("is empty: " + m.isEmpty());
    System.out.println("without duplicates: " + m.toStringWithoutDuplicates());
    System.out.println("entry set: "  + m.entrySet());
    System.out.println("entry list: "  + m.entryList());
    System.out.println("number of elements for key \"boris\": " + m.getNumberOfKeyElements(m.getAddressOfKey("boris")));
    m.removeKeyWithValue("c", "C");
    m.removeKeyWithValue("boris", "Beate");
    m.removeKeyWithValue("boris", "Brit");
    m.removeKeyWithValue("z", "Z");
    System.out.println("remove (c,C), (boris,Beate), (boris,Brit), (z,Z):");
    System.out.println(m);
    System.out.println("entry set: "  + m.entrySet());
    System.out.println("key set: "  + m.keySet());
    System.out.println("values: "  + m.values());
    System.out.println("values list for key \"f\": "  + m.getValuesList("f"));
    System.out.println("number of elements for key \"boris\": " + m.getNumberOfKeyElements(m.getAddressOfKey("boris")));
    System.out.println("without duplicates: " + m.toStringWithoutDuplicates());
    m.removeAllDuplicates("alfons", "Alf");
    m.removeAllDuplicates("f", "Fred");
    m.removeAllDuplicates("t", "T");
    System.out.println("remove all duplicates of (alfons,Alf), (f,Fred), (t,T):");
    System.out.println(m);
    m.put("e", "Emil");
    m.put("alfons", "Alberta");
    System.out.println("put (e,Emil), (alfons,Alberta):");
    System.out.println(m);
    System.out.println("size: " + m.size());
    // test keys with same hashCode % 1024
    m.put("a", "B");
    m.put("1002", "B");
    m.put("1123", "B");
    System.out.println("put (a,B), (1002,B) (1123,B) with same hashCode % 1024:");
    System.out.println("a\t hashCode % 1024 :\t"  + (Math.abs("a".hashCode()) % 1024));
    System.out.println("1002\t hashCode % 1024 :\t" + (Math.abs("1002".hashCode()) % 1024));
    System.out.println("1123\t hashCode % 1024 :\t" + (Math.abs("1123".hashCode()) % 1024));
    System.out.println(m);
    System.out.println("size: " + m.size());
    // test remove(key)
    m.remove("d");
    System.out.println("remove(d): " + m);
    m.remove("a");
    System.out.println("remove(a): " + m);
    m.remove("1123");
    System.out.println("remove(1123): " + m);
    m.put("a", "B");
    m.put("1123", "B");
    System.out.println("put (a,B), (1123,B) with same hashCode % 1024 (to test remove(key)):");
    System.out.println(m);
    m.remove("a");
    System.out.println("remove(a): " + m);
    System.out.println("size: " + m.size());
    // test method getCollection (class GetCollection)
    System.out.println("test getValuesList(\"alfons\"): " + m.getValuesList("alfons"));
    System.out.println("test getCollection(\"alfons\"): " + m.getCollection("alfons"));
    m.getCollection("alfons").add("Anton");
    System.out.println("getCollection(\"alfons\").add(\"Anton\"): \n" + m);
    m.getCollection("f").clear();
    System.out.println("getCollection(\"f\").clear(): \n" + m);
    System.out.println("getCollection(\"boris\").contains(\"A\"): " + m.getCollection("boris").contains("A"));
    System.out.println("getCollection(\"boris\").contains(\"Bertha\"): " + m.getCollection("boris").contains("Bertha"));
    System.out.println("getCollection(\"f\").isEmpty(): " + m.getCollection("f").isEmpty());
    m.getCollection("boris").remove("B");
    System.out.println("getCollection(\"boris\").remove(\"B\"): \n" + m);
    m.clear();
    System.out.println("clear: " + m);
    BufferManager.getBufferManager().writeAllModifiedPages();
  }
}
TOP

Related Classes of lupos.datastructures.paged_map.PagedHashMultiMap

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.