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