/**
   * {@inheritDoc}
   *
   * <p>This method will, on a best-effort basis, throw a {@link
   * java.util.ConcurrentModificationException} if the mapping function modified this map during
   * computation.
   *
   * @throws ConcurrentModificationException if it is detected that the mapping function modified
   *     this map
   */
  @Override
  public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
    Objects.requireNonNull(mappingFunction);

    Entry<?, ?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K, V> e = (Entry<K, V>) tab[index];
    for (; e != null; e = e.next) {
      if (e.hash == hash && e.key.equals(key)) {
        // Hashtable not accept null value
        return e.value;
      }
    }

    int mc = modCount;
    V newValue = mappingFunction.apply(key);
    if (mc != modCount) {
      throw new ConcurrentModificationException();
    }
    if (newValue != null) {
      addEntry(hash, key, newValue, index);
    }

    return newValue;
  }
  /**
   * {@inheritDoc}
   *
   * <p>This method will, on a best-effort basis, throw a {@link
   * java.util.ConcurrentModificationException} if the remapping function modified this map during
   * computation.
   *
   * @throws ConcurrentModificationException if it is detected that the remapping function modified
   *     this map
   */
  @Override
  public synchronized V compute(
      K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);

    Entry<?, ?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K, V> e = (Entry<K, V>) tab[index];
    for (Entry<K, V> prev = null; e != null; prev = e, e = e.next) {
      if (e.hash == hash && Objects.equals(e.key, key)) {
        int mc = modCount;
        V newValue = remappingFunction.apply(key, e.value);
        if (mc != modCount) {
          throw new ConcurrentModificationException();
        }
        if (newValue == null) {
          if (prev != null) {
            prev.next = e.next;
          } else {
            tab[index] = e.next;
          }
          modCount = mc + 1;
          count--;
        } else {
          e.value = newValue;
        }
        return newValue;
      }
    }

    int mc = modCount;
    V newValue = remappingFunction.apply(key, null);
    if (mc != modCount) {
      throw new ConcurrentModificationException();
    }
    if (newValue != null) {
      addEntry(hash, key, newValue, index);
    }

    return newValue;
  }
  @Override
  public synchronized V putIfAbsent(K key, V value) {
    Objects.requireNonNull(value);

    // Makes sure the key is not already in the hashtable.
    Entry<?, ?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K, V> entry = (Entry<K, V>) tab[index];
    for (; entry != null; entry = entry.next) {
      if ((entry.hash == hash) && entry.key.equals(key)) {
        V old = entry.value;
        if (old == null) {
          entry.value = value;
        }
        return old;
      }
    }

    addEntry(hash, key, value, index);
    return null;
  }
  /**
   * Maps the specified {@code key} to the specified {@code value} in this hashtable. Neither the
   * key nor the value can be {@code null}.
   *
   * <p>The value can be retrieved by calling the {@code get} method with a key that is equal to the
   * original key.
   *
   * @param key the hashtable key
   * @param value the value
   * @return the previous value of the specified key in this hashtable, or {@code null} if it did
   *     not have one
   * @exception NullPointerException if the key or value is {@code null}
   * @see Object#equals(Object)
   * @see #get(Object)
   */
  public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
      throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?, ?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K, V> entry = (Entry<K, V>) tab[index];
    for (; entry != null; entry = entry.next) {
      if ((entry.hash == hash) && entry.key.equals(key)) {
        V old = entry.value;
        entry.value = value;
        return old;
      }
    }

    addEntry(hash, key, value, index);
    return null;
  }