001/* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.GwtCompatible; 022import com.google.common.annotations.GwtIncompatible; 023import com.google.common.annotations.J2ktIncompatible; 024import com.google.errorprone.annotations.CanIgnoreReturnValue; 025import java.io.IOException; 026import java.io.ObjectInputStream; 027import java.io.ObjectOutputStream; 028import java.util.EnumMap; 029import java.util.HashMap; 030import java.util.Map; 031import javax.annotation.CheckForNull; 032import org.checkerframework.checker.nullness.qual.Nullable; 033 034/** 035 * A {@code BiMap} backed by an {@code EnumMap} instance for keys-to-values, and a {@code HashMap} 036 * instance for values-to-keys. Null keys are not permitted, but null values are. An {@code 037 * EnumHashBiMap} and its inverse are both serializable. 038 * 039 * <p>See the Guava User Guide article on <a href= 040 * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap">{@code BiMap}</a>. 041 * 042 * @author Mike Bostock 043 * @since 2.0 044 */ 045@GwtCompatible(emulated = true) 046@J2ktIncompatible 047@ElementTypesAreNonnullByDefault 048public final class EnumHashBiMap<K extends Enum<K>, V extends @Nullable Object> 049 extends AbstractBiMap<K, V> { 050 transient Class<K> keyTypeOrObjectUnderJ2cl; 051 052 /** 053 * Returns a new, empty {@code EnumHashBiMap} using the specified key type. 054 * 055 * @param keyType the key type 056 */ 057 public static <K extends Enum<K>, V extends @Nullable Object> EnumHashBiMap<K, V> create( 058 Class<K> keyType) { 059 return new EnumHashBiMap<>(keyType); 060 } 061 062 /** 063 * Constructs a new bimap with the same mappings as the specified map. If the specified map is an 064 * {@code EnumHashBiMap} or an {@link EnumBiMap}, the new bimap has the same key type as the input 065 * bimap. Otherwise, the specified map must contain at least one mapping, in order to determine 066 * the key type. 067 * 068 * @param map the map whose mappings are to be placed in this map 069 * @throws IllegalArgumentException if map is not an {@code EnumBiMap} or an {@code EnumHashBiMap} 070 * instance and contains no mappings 071 */ 072 public static <K extends Enum<K>, V extends @Nullable Object> EnumHashBiMap<K, V> create( 073 Map<K, ? extends V> map) { 074 EnumHashBiMap<K, V> bimap = create(EnumBiMap.inferKeyTypeOrObjectUnderJ2cl(map)); 075 bimap.putAll(map); 076 return bimap; 077 } 078 079 private EnumHashBiMap(Class<K> keyType) { 080 super(new EnumMap<K, V>(keyType), new HashMap<V, K>()); 081 // TODO: cpovirk - Pre-size the HashMap based on the number of enum values? 082 this.keyTypeOrObjectUnderJ2cl = keyType; 083 } 084 085 // Overriding these 3 methods to show that values may be null (but not keys) 086 087 @Override 088 K checkKey(K key) { 089 return checkNotNull(key); 090 } 091 092 @CanIgnoreReturnValue 093 @Override 094 @SuppressWarnings("RedundantOverride") // b/192446478: RedundantOverride ignores some annotations. 095 // TODO(b/192446998): Remove this override after tools understand nullness better. 096 @CheckForNull 097 public V put(K key, @ParametricNullness V value) { 098 return super.put(key, value); 099 } 100 101 @CanIgnoreReturnValue 102 @Override 103 @SuppressWarnings("RedundantOverride") // b/192446478: RedundantOverride ignores some annotations. 104 // TODO(b/192446998): Remove this override after tools understand nullness better. 105 @CheckForNull 106 public V forcePut(K key, @ParametricNullness V value) { 107 return super.forcePut(key, value); 108 } 109 110 /** Returns the associated key type. */ 111 @GwtIncompatible 112 public Class<K> keyType() { 113 return keyTypeOrObjectUnderJ2cl; 114 } 115 116 /** 117 * @serialData the key class, number of entries, first key, first value, second key, second value, 118 * and so on. 119 */ 120 @GwtIncompatible // java.io.ObjectOutputStream 121 private void writeObject(ObjectOutputStream stream) throws IOException { 122 stream.defaultWriteObject(); 123 stream.writeObject(keyTypeOrObjectUnderJ2cl); 124 Serialization.writeMap(this, stream); 125 } 126 127 @SuppressWarnings("unchecked") // reading field populated by writeObject 128 @GwtIncompatible // java.io.ObjectInputStream 129 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 130 stream.defaultReadObject(); 131 keyTypeOrObjectUnderJ2cl = (Class<K>) stream.readObject(); 132 /* 133 * TODO: cpovirk - Pre-size the HashMap based on the number of enum values? (But *not* based on 134 * the number of entries in the map, as that makes it easy for hostile inputs to trigger lots of 135 * allocation—not that any program should be deserializing hostile inputs to begin with!) 136 */ 137 setDelegates(new EnumMap<K, V>(keyTypeOrObjectUnderJ2cl), new HashMap<V, K>()); 138 Serialization.populateMap(this, stream); 139 } 140 141 @GwtIncompatible // only needed in emulated source. 142 private static final long serialVersionUID = 0; 143}