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

ch.ethz.sn.visone3.lang.impl.containers.LongHashMap Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of netroles.
 *
 * netroles is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * netroles 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with netroles.  If not, see .
 */

package ch.ethz.sn.visone3.lang.impl.containers;

import ch.ethz.sn.visone3.lang.LongMap;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Hash map with quadratic probing. The probing scheme ensures every position is probed if the size
 * is a power of 2.
 */
final class LongHashMap implements LongMap {
  @SuppressWarnings("unused")
  private static final byte FREE = 0b0_00; // 0
  private static final byte DIRT = 0b0_10; // 2
  private static final byte FULL = 0b0_11; // 3
  private static final byte MASK_INSERT = 0b0_01;
  private static final byte MASK_SEARCH = 0b0_10;
  private final float minLoad;
  private final float maxLoad;
  private long[] keys;
  private T[] values;
  private byte[] state;
  private int size;

  public LongHashMap() {
    this(1 << 4);
  }

  public LongHashMap(final int size) {
    this(size, .4f, .9f);
  }

  /**
   * Constructs a new long hash map with initial size/capacity.
   * 
   * @param size
   *          Initial size.
   */
  @SuppressWarnings("unchecked")
  public LongHashMap(final int size, final float minLoad, final float maxLoad) {
    this.minLoad = minLoad;
    this.maxLoad = maxLoad;
    keys = new long[size];
    values = (T[]) new Object[size];
    state = new byte[size];
  }

  @SuppressWarnings("unchecked")
  private void rehash(final int size) {
    final long[] keys = this.keys;
    final T[] values = this.values;
    final byte[] state = this.state;
    this.keys = new long[size];
    this.values = (T[]) new Object[size];
    this.state = new byte[size];
    this.size = 0;
    for (int i = 0; i < keys.length; i++) {
      if (state[i] == FULL) {
        put(keys[i], values[i]);
      }
    }
  }

  @Override
  public T put(final long key, final T value) {
    if (size >= (int) (maxLoad * keys.length)) {
      rehash(keys.length << 1);
    }
    // search the value, a FREE or a DIRT position
    final int i = search(key, MASK_INSERT);
    if (i >= 0 && state[i] != FULL) {
      keys[i] = key;
      values[i] = value;
      state[i] = FULL;
      ++size;
      return null;
    } else if (i < 0 || keys[i] != key) {
      throw new IllegalStateException("probing failed to find position " + this);
    } else {
      T oldValue = values[i];
      values[i] = value;
      return oldValue;
    }
  }

  @Override
  public T get(final long key) {
    // search the value or a FREE position
    final int i = search(key, MASK_SEARCH);
    if (i >= 0 && state[i] == FULL) {
      return values[i];
    }
    return null;
  }

  @Override
  public T getOrDefault(final long key, final T def) {
    // search the value or a FREE position
    final int i = search(key, MASK_SEARCH);
    if (i >= 0 && state[i] == FULL) {
      return values[i];
    }
    return def;
  }

  @Override
  public T remove(final long key) {
    // search the value or a FREE position
    final int i = search(key, MASK_SEARCH);
    if (i >= 0 && state[i] == FULL) {
      final T val = values[i];
      values[i] = null;
      state[i] = DIRT;
      --size;
      if (size < (int) (minLoad * keys.length)) {
        rehash(keys.length >>> 1);
      }
      return val;
    }
    return null;
  }

  @Override
  public boolean contains(final long key) {
    // search the key or a FREE position
    final int i = search(key, MASK_SEARCH);
    return i >= 0 && state[i] == FULL;
  }

  /**
   * Search the index for {@code value}.
   *
   * @return The first index in the probing sequence where either {@code value} is located or the
   *         masked state is 0.
   */
  protected int search(final long value, final byte mask) {
    int h = ((int) value >>> 1) % keys.length;
    int i = 0;
    while (keys[h] != value && (state[h] & mask) != 0 && i < keys.length) {
      ++i;
      h = (h + i) % keys.length;
    }
    if (i >= keys.length) {
      return -1;
    }
    return h;
  }

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

  @Override
  public String toString() {
    return IntStream.range(0, keys.length).mapToObj(i -> "{" + keys[i] + "|" + state[i] + "}") //
        .collect(Collectors.joining(","));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy