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

org.hibernate.internal.util.collections.IdentityMap Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.internal.util.collections;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * A Map where keys are compared by object identity,
 * rather than equals().
 */
public final class IdentityMap implements Map {

	private final LinkedHashMap,V> map;
	@SuppressWarnings( {"unchecked"})
	private transient Map.Entry,V>[] entryArray = null;

	/**
	 * Return a new instance of this class, with iteration
	 * order defined as the order in which entries were added
	 *
	 * @param size The size of the map to create
	 * @return The map
	 */
	public static  IdentityMap instantiateSequenced(int size) {
		return new IdentityMap( new LinkedHashMap<>( size << 1, 0.6f ) );
	}

	/**
	 * Private ctor used in serialization.
	 *
	 * @param underlyingMap The delegate map.
	 */
	private IdentityMap(LinkedHashMap,V> underlyingMap) {
		map = underlyingMap;
	}

	/**
	 * Return the map entries (as instances of Map.Entry in a collection that
	 * is safe from concurrent modification). ie. we may safely add new instances to
	 * the underlying Map during iteration of the entries().
	 *
	 * @param map The map of entries
	 * @return Collection
	 */
	public static  Map.Entry[] concurrentEntries(Map map) {
		return ( (IdentityMap) map ).entryArray();
	}

	public static  void onEachKey(Map map, Consumer consumer) {
		final IdentityMap identityMap = (IdentityMap) map;
		identityMap.map.forEach( (kIdentityKey, v) -> consumer.accept( kIdentityKey.key ) );
	}

	/**
	 * Override Map{@link #forEach(BiConsumer)} to provide a more efficient implementation
	 * @param action the operation to apply to each element
	 */
	@Override
	public void forEach(BiConsumer action) {
		map.forEach( (k,v) -> action.accept( k.key, v ) );
	}

	public Iterator keyIterator() {
		return new KeyIterator( map.keySet().iterator() );
	}

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

	@Override
	public boolean isEmpty() {
		return map.isEmpty();
	}

	@Override
	@SuppressWarnings({ "unchecked" })
	public boolean containsKey(Object key) {
		return map.containsKey( new IdentityKey( key ) );
	}

	@Override
	public boolean containsValue(Object val) {
		throw new UnsupportedOperationException( "Avoid this operation: does not perform well" );
		//return map.containsValue( val );
	}

	@Override
	@SuppressWarnings( {"unchecked"})
	public V get(Object key) {
		return map.get( new IdentityKey( key ) );
	}

	@Override
	public V put(K key, V value) {
		this.entryArray = null;
		return map.put( new IdentityKey( key ), value );
	}

	@Override
	@SuppressWarnings( {"unchecked"})
	public V remove(Object key) {
		this.entryArray = null;
		return map.remove( new IdentityKey( key ) );
	}

	@Override
	public void putAll(Map otherMap) {
		for ( Entry entry : otherMap.entrySet() ) {
			put( entry.getKey(), entry.getValue() );
		}
	}

	@Override
	public void clear() {
		entryArray = null;
		map.clear();
	}

	@Override
	public Set keySet() {
		// would need an IdentitySet for this!
		// (and we just don't use this method so it's ok)
		throw new UnsupportedOperationException();
	}

	@Override
	public Collection values() {
		return map.values();
	}

	@Override
	public Set> entrySet() {
		Set> set = new HashSet>( map.size() );
		for ( Entry, V> entry : map.entrySet() ) {
			set.add( new IdentityMapEntry( entry.getKey().key, entry.getValue() ) );
		}
		return set;
	}

	@SuppressWarnings( {"unchecked"})
	public Map.Entry[] entryArray() {
		if ( entryArray == null ) {
			entryArray = new Map.Entry[ map.size() ];
			final Iterator, V>> itr = map.entrySet().iterator();
			int i = 0;
			while ( itr.hasNext() ) {
				final Entry, V> me = itr.next();
				entryArray[i++] = new IdentityMapEntry( me.getKey().key, me.getValue() );
			}
		}
		return entryArray;
	}

	@Override
	public String toString() {
		return map.toString();
	}

	private static final class KeyIterator implements Iterator {
		private final Iterator> identityKeyIterator;

		private KeyIterator(Iterator> iterator) {
			identityKeyIterator = iterator;
		}

		public boolean hasNext() {
			return identityKeyIterator.hasNext();
		}

		public K next() {
			return identityKeyIterator.next().key;
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}

	}

	private static final class IdentityMapEntry implements java.util.Map.Entry {

		private final K key;
		private final V value;

		IdentityMapEntry(final K key, final V value) {
			this.key=key;
			this.value=value;
		}

		public K getKey() {
			return key;
		}

		public V getValue() {
			return value;
		}

		public V setValue(final V value) {
			throw new UnsupportedOperationException();
		}
	}

	/**
	 * We need to base the identity on {@link System#identityHashCode(Object)}
	 */
	private static final class IdentityKey implements Serializable {

		private final K key;

		IdentityKey(K key) {
			this.key = key;
		}

		@SuppressWarnings( {"EqualsWhichDoesntCheckParameterClass"})
		@Override
		public boolean equals(Object other) {
			return other != null && key == ( (IdentityKey) other ).key;
		}

		@Override
		public int hashCode() {
			return System.identityHashCode( key );
		}

		@Override
		public String toString() {
			return key.toString();
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy