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

org.libj.util.BiMap Maven / Gradle / Ivy

Go to download

Supplementary utilities for classes that belong to java.util, or are considered essential as to justify existence in java.util.

There is a newer version: 0.9.1
Show newest version
/* Copyright (c) 2017 LibJ
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * You should have received a copy of The MIT License (MIT) along with this
 * program. If not, see .
 */

package org.libj.util;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

/**
 * Bidirectional map that maintains both key->value and value->key
 * mappings. This implementation utilizes the mechanisms of the
 * {@link ObservableMap} to guarantee operational symmetry between the
 * {@code this} map and the {@link #inverse()} map. Methods defined in the
 * {@code Map} interface that result in a mutation to the {@code this} instance
 * will also result in reflected operations to the {@link #inverse()} instance.
 * This implementation is not synchronized.
 *
 * @param  The type of keys maintained by this map.
 * @param  The type of mapped values.
 * @see ObservableMap
 * @see DelegateMap
 */
public abstract class BiMap extends DelegateMap {
  protected volatile BiMap inverse;

  /**
   * Construct a new bidirectional map with the provided source maps.
   *
   * @param forward The forward source map.
   * @param reverse The reverse source map.
   */
  protected BiMap(final Map forward, final Map reverse) {
    inverse = newEmptyInverseMap();
    setTarget(forward);
    inverse.setTarget(reverse);
    inverse.inverse = this;
  }

  /**
   * Creates an empty instance.
   */
  protected BiMap() {
  }

  /**
   * Sets the specified map as the underlying target map of this {@code BiMap}.
   *
   * @param map The map to set at the underlying target of this {@code BiMap}.
   */
  protected void setTarget(final Map map) {
    super.target = new ObservableMap(map) {
      @Override
      protected boolean beforePut(final K key, final V oldValue, final V newValue) {
        ((ObservableMap)BiMap.this.inverse.target).target.put(newValue, key);
        if (oldValue != null)
          ((ObservableMap)BiMap.this.inverse.target).target.remove(oldValue);

        return true;
      }

      @Override
      protected void afterRemove(final Object key, final V value, final RuntimeException re) {
        ((ObservableMap)BiMap.this.inverse.target).target.remove(value);
      }
    };
  }

  /**
   * Returns a new instance of an empty inverse subclass of {@code BiMap}.
   *
   * @return A new instance of an empty inverse {@code BiMap}.
   */
  protected abstract BiMap newEmptyInverseMap();

  /**
   * Returns the inverse of this map, maintaining value->key mappings.
   * Mutations to the {@code inverse()} map are reflected in {@code this} map.
   *
   * @return The inverse map.
   */
  public Map inverse() {
    return inverse;
  }

  @Override
  @SuppressWarnings("unlikely-arg-type")
  public boolean containsValue(final Object value) {
    return inverse.containsKey(value);
  }

  protected ObservableSet keySet;

  @Override
  public Set keySet() {
    return keySet == null ? keySet = new ObservableSet(target.keySet()) {
      @Override
      @SuppressWarnings("unchecked")
      protected void afterRemove(final Object o, final RuntimeException re) {
        BiMap.this.inverse.target.remove(((Map.Entry)o).getValue());
      }
    } : keySet;
  }

  protected ObservableCollection values;

  @Override
  public Collection values() {
    return values == null ? values = new ObservableCollection(target.values()) {
      @Override
      protected void afterRemove(final Object o, final RuntimeException re) {
        BiMap.this.inverse.target.remove(o);
      }
    } : values;
  }

  protected volatile ObservableSet> entrySet;

  @Override
  public Set> entrySet() {
    return entrySet == null ? entrySet = new ObservableSet>(target.entrySet()) {
      @Override
      @SuppressWarnings("unchecked")
      protected void afterRemove(final Object o, final RuntimeException re) {
        BiMap.this.inverse.target.remove(((Map.Entry)o).getValue());
      }
    } : entrySet;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy