1 /* 2 * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.util; 27 28 import java.util.Map; 29 import java.util.Objects; 30 31 /** 32 * An immutable container for a key and a value, both of which are nullable. 33 * 34 * <p>This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a> 35 * class; programmers should treat instances that are 36 * {@linkplain #equals(Object) equal} as interchangeable and should not 37 * use instances for synchronization, or unpredictable behavior may 38 * occur. For example, in a future release, synchronization may fail. 39 * 40 * @apiNote 41 * This class is not exported. Instances are created by various Map implementations 42 * when they need a Map.Entry that isn't connected to the Map. 43 * 44 * <p>This class differs from AbstractMap.SimpleImmutableEntry in that it is not 45 * serializable and that it is final. This class differs from java.util.KeyValueHolder 46 * in that the key and value are nullable. 47 * 48 * <p>In principle this class could be a variation on KeyValueHolder. However, 49 * making that class selectively support nullable keys and values is quite intricate. 50 * Various specifications (such as Map.ofEntries and Map.entry) specify non-nullability 51 * of the key and the value. Map.Entry.copyOf also requires non-null keys and values; 52 * but it simply passes through KeyValueHolder instances, assuming their keys and values 53 * are non-nullable. If a KVH with nullable keys and values were introduced, some way 54 * to distinguish it would be necessary. This could be done by introducing a subclass 55 * (requiring KVH to be made non-final) or by introducing some kind of "mode" field 56 * (potentially increasing the size of every KVH instance, though another field could 57 * probably fit into the object's padding in most JVMs.) More critically, a mode field 58 * would have to be checked in all the right places to get the right behavior. 59 * 60 * <p>A longer range possibility is to selectively relax the restrictions against nulls in 61 * Map.entry and Map.Entry.copyOf. This would also require some intricate specification 62 * changes and corresponding implementation changes (e.g., the implementations backing 63 * Map.of might still need to reject nulls, and so would Map.ofEntries) but allowing 64 * a Map.Entry itself to contain nulls seems beneficial in general. If this is done, 65 * merging KeyValueHolder and NullableKeyValueHolder should be reconsidered. 66 * 67 * @param <K> the key type 68 * @param <V> the value type 69 */ 70 @jdk.internal.ValueBased 71 public final class NullableKeyValueHolder<K,V> implements Map.Entry<K,V> { 72 final K key; 73 final V value; 74 75 /** 76 * Constructs a NullableKeyValueHolder. 77 * 78 * @param k the key, may be null 79 * @param v the value, may be null 80 */ NullableKeyValueHolder(K k, V v)81 public NullableKeyValueHolder(K k, V v) { 82 key = k; 83 value = v; 84 } 85 86 /** 87 * Constructs a NullableKeyValueHolder from a Map.Entry. No need for an 88 * idempotent copy at this time. 89 * 90 * @param entry the entry, must not be null 91 */ NullableKeyValueHolder(Map.Entry<K,V> entry)92 public NullableKeyValueHolder(Map.Entry<K,V> entry) { 93 Objects.requireNonNull(entry); 94 key = entry.getKey(); 95 value = entry.getValue(); 96 } 97 98 /** 99 * Gets the key from this holder. 100 * 101 * @return the key, may be null 102 */ 103 @Override getKey()104 public K getKey() { 105 return key; 106 } 107 108 /** 109 * Gets the value from this holder. 110 * 111 * @return the value, may be null 112 */ 113 @Override getValue()114 public V getValue() { 115 return value; 116 } 117 118 /** 119 * Throws {@link UnsupportedOperationException}. 120 * 121 * @param value ignored 122 * @return never returns normally 123 */ 124 @Override setValue(V value)125 public V setValue(V value) { 126 throw new UnsupportedOperationException("not supported"); 127 } 128 129 /** 130 * Compares the specified object with this entry for equality. 131 * Returns {@code true} if the given object is also a map entry and 132 * the two entries' keys and values are equal. 133 */ 134 @Override equals(Object o)135 public boolean equals(Object o) { 136 return o instanceof Map.Entry<?, ?> e 137 && Objects.equals(key, e.getKey()) 138 && Objects.equals(value, e.getValue()); 139 } 140 hash(Object obj)141 private int hash(Object obj) { 142 return (obj == null) ? 0 : obj.hashCode(); 143 } 144 145 /** 146 * Returns the hash code value for this map entry. The hash code 147 * is {@code key.hashCode() ^ value.hashCode()}. 148 */ 149 @Override hashCode()150 public int hashCode() { 151 return hash(key) ^ hash(value); 152 } 153 154 /** 155 * Returns a String representation of this map entry. This 156 * implementation returns the string representation of this 157 * entry's key followed by the equals character ("{@code =}") 158 * followed by the string representation of this entry's value. 159 * 160 * @return a String representation of this map entry 161 */ 162 @Override toString()163 public String toString() { 164 return key + "=" + value; 165 } 166 } 167