Package propel.core.collections.maps.primitive

Source Code of propel.core.collections.maps.primitive.LongHashMap$BucketEntry

// /////////////////////////////////////////////////////////
// This file is part of Propel.
//
// Propel is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Propel is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Propel. If not, see <http://www.gnu.org/licenses/>.
// /////////////////////////////////////////////////////////
// Authored by: Nikolaos Tountas -> salam.kaser-at-gmail.com
// /////////////////////////////////////////////////////////
package propel.core.collections.maps.primitive;

import lombok.Validate;
import lombok.Validate.NotNull;
import lombok.val;
import propel.core.collections.lists.ReifiedArrayList;
import propel.core.utils.ArrayUtils;
import propel.core.utils.SuperTypeToken;
import propel.core.utils.SuperTypeTokenException;

/**
* Implementation of a long -> V hash map with an emphasis on lookup speed and reduction of memory footprint.
*/
public class LongHashMap<V>
    implements ILongHashMap<V>
{
  /**
   * The maximum capacity, used if a higher value is implicitly specified by either of the constructors with arguments
   */
  private static final int MAXIMUM_CAPACITY = 1 << 30;
  private final Class<?> genericTypeParameterValue;
  private BucketEntry[] table;
  private int size;

  /**
   * Constructs an empty map with the specified max capacity
   *
   * @throws NullPointerException An argument is null
   * @throws IllegalArgumentException An argument is invalid
   * @throws SuperTypeTokenException When called without using anonymous class semantics.
   */
  public LongHashMap(final int maxCapacity)
  {
    this.genericTypeParameterValue = SuperTypeToken.getClazz(getClass());
    init(maxCapacity);
  }

  /**
   * Constructs an empty map with the specified max capacity.
   *
   * @throws NullPointerException An argument is null
   * @throws IllegalArgumentException An argument is invalid
   */
  @Validate
  public LongHashMap(final int maxCapacity, @NotNull final Class<?> genericTypeParameterValue)
  {
    this.genericTypeParameterValue = genericTypeParameterValue;
    init(maxCapacity);
  }

  /**
   * Initialisation moved here for re-use
   */
  private void init(final int maxCapacity)
  {
    if (maxCapacity < 0)
      throw new IllegalArgumentException("maxCapacity=" + maxCapacity);

    // Find a power of 2 >= maxSize
    int capacity = 1;
    while (capacity < maxCapacity)
      capacity <<= 1;

    // must not be over this maximum
    if (capacity > MAXIMUM_CAPACITY)
      capacity = MAXIMUM_CAPACITY;

    this.table = ArrayUtils.create(BucketEntry.class, capacity);
  }

  /**
   * Clears the contents. This is an O(n) operation
   */
  public void clear()
  {
    // clear all bucket entries
    for (int i = 0; i < table.length; i++)
      table[i] = null;

    size = 0;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(1) operation
   */
  @Override
  public V put(final long key, final V value)
  {
    // find bucket entry
    int index = indexFor(key);
    if (table[index] == null)
      table[index] = new BucketEntry();

    // the new long->V entry
    val inserted = new LongEntry<V>(key, value);

    // attempt to find old long->V entry, if existent, to replace it
    BucketEntry bucket = table[index];
    int existingIndex = bucket.find(key);

    // if existent, replace
    if (existingIndex >= 0)
    {
      // this does not increase the hash map size
      val oldItem = bucket.set(existingIndex, inserted);
      return oldItem.value;
    }

    // otherwise insert new, this increases the size
    bucket.add(inserted);
    size++;
    return null;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(1) operation
   */
  @Override
  public boolean containsKey(final long key)
  {
    int index = indexFor(key);
    if (table[index] != null)
      return table[index].find(key) >= 0;

    return false;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(n) operation
   */
  @Override
  @Validate
  public LongEntry<V> containsValue(@NotNull final V value)
  {
    for (int i = 0; i < table.length; i++)
      if (table[i] != null)
        for (int j = 0; j < table[i].position; j++)
        {
          val item = table[i].entries[j];
          if (item.value != null)
            if (item.value.equals(value))
              return item;
        }

    return null;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(n) operation
   */
  @Override
  public LongEntry<V>[] entries()
  {
    val result = new ReifiedArrayList<LongEntry<V>>(size, LongEntry.class);
    for (int i = 0; i < table.length; i++)
      if (table[i] != null)
        result.addAll(table[i].entries);

    return result.toArray();
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(1) operation
   */
  @Override
  public V get(long key)
  {
    BucketEntry bucket = table[indexFor(key)];
    if (bucket != null)
      for (int i = 0; i < bucket.position; i++)
      {
        val entry = bucket.entries[i];
        if (entry.key == key)
          return entry.value;
      }

    return null;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(1) operation
   */
  @Override
  public boolean isEmpty()
  {
    return size == 0;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(n) operation
   */
  @Override
  public V[] values()
  {
    val result = new ReifiedArrayList<V>(size, genericTypeParameterValue);
    for (int i = 0; i < table.length; i++)
      if (table[i] != null)
        for (int j = 0; j < table[i].position; j++)
        {
          val item = table[i].entries[j];
          if (item != null)
            result.add(item.value);
        }

    return result.toArray();
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(n) operation
   */
  @Override
  public long[] keySet()
  {
    int index = 0;
    long[] result = new long[size];
    for (int i = 0; i < table.length; i++)
      if (table[i] != null)
        for (int j = 0; j < table[i].position; j++)
        {
          val item = table[i].entries[j];
          result[index++] = item.key;
        }

    return result;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(1) operation
   */
  @Override
  public int size()
  {
    return size;
  }

  /**
   * {@inheritDoc}
   *
   * This is an O(1) operation
   */
  public int capacity()
  {
    return table.length;
  }

  /**
   * Resizes the hash map.
   *
   * This is an O(n) operation, note that it more than doubles the space used for a short while
   */
  public void resize(int capacity)
  {
    BucketEntry[] tableCopy = ArrayUtils.clone(table);
    init(capacity);
    for (BucketEntry bucket : tableCopy)
      if (bucket != null)
        for (val item : bucket.entries)
          put(item.key, item.value);
  }

  /**
   * Returns index for hash code h.
   */
  private int indexFor(long key)
  {
    return ((int) key / 2) & (table.length - 1);
  }

  /**
   * The hash map contains multiple bucket entry classes such as this
   */
  final class BucketEntry
  {
    long[] keys;
    LongEntry<V>[] entries;
    int position;

    BucketEntry()
    {
      keys = new long[8];
      entries = ArrayUtils.create(LongEntry.class, 8);
      position = 0;
    }

    void add(LongEntry<V> entry)
    {
      keys[position] = entry.key;
      entries[position] = entry;
      position++;

      // ensure capacity
      if (position >= entries.length)
      {
        val newKeys = new long[(int) (keys.length * 0.75f)];
        System.arraycopy(keys, 0, newKeys, 0, entries.length);
        keys = newKeys;
        entries = ArrayUtils.resize(entries, entries.length * 2);
      }
    }

    int find(long key)
    {
      for (int i = 0; i < position; i++)
        if (keys[i] == key)
          return i;

      return -1;
    }

    LongEntry<V> set(int index, LongEntry<V> entry)
    {
      keys[index] = entry.key;
      val old = entries[index];
      entries[index] = entry;
      return old;
    }
  }
 
}
TOP

Related Classes of propel.core.collections.maps.primitive.LongHashMap$BucketEntry

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.