1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 /*
19 * This is not the original file distributed by the Apache Software Foundation
20 * It has been modified by the Hipparchus project
21 */
22
23 package org.hipparchus.util;
24
25 import java.io.IOException;
26 import java.io.ObjectInputStream;
27 import java.io.Serializable;
28 import java.util.Arrays;
29 import java.util.ConcurrentModificationException;
30 import java.util.NoSuchElementException;
31
32 /**
33 * Open addressed map from int to double.
34 * <p>This class provides a dedicated map from integers to doubles with a
35 * much smaller memory overhead than standard <code>java.util.Map</code>.</p>
36 * <p>This class is not synchronized. The specialized iterators returned by
37 * {@link #iterator()} are fail-fast: they throw a
38 * <code>ConcurrentModificationException</code> when they detect the map has been
39 * modified during iteration.</p>
40 */
41 public class OpenIntToDoubleHashMap extends AbstractOpenIntHashMap implements Serializable {
42
43 /** Serializable version identifier */
44 private static final long serialVersionUID = 20240326L;
45
46 /** Values table. */
47 private double[] values;
48
49 /** Return value for missing entries. */
50 private final double missingEntries;
51
52 /**
53 * Build an empty map with default size and using NaN for missing entries.
54 */
55 public OpenIntToDoubleHashMap() {
56 this(DEFAULT_EXPECTED_SIZE, Double.NaN);
57 }
58
59 /**
60 * Build an empty map with default size
61 * @param missingEntries value to return when a missing entry is fetched
62 */
63 public OpenIntToDoubleHashMap(final double missingEntries) {
64 this(DEFAULT_EXPECTED_SIZE, missingEntries);
65 }
66
67 /**
68 * Build an empty map with specified size and using NaN for missing entries.
69 * @param expectedSize expected number of elements in the map
70 */
71 public OpenIntToDoubleHashMap(final int expectedSize) {
72 this(expectedSize, Double.NaN);
73 }
74
75 /**
76 * Build an empty map with specified size.
77 * @param expectedSize expected number of elements in the map
78 * @param missingEntries value to return when a missing entry is fetched
79 */
80 public OpenIntToDoubleHashMap(final int expectedSize,
81 final double missingEntries) {
82 super(expectedSize);
83 values = new double[getCapacity()];
84 this.missingEntries = missingEntries;
85 }
86
87 /**
88 * Copy constructor.
89 * @param source map to copy
90 */
91 public OpenIntToDoubleHashMap(final OpenIntToDoubleHashMap source) {
92 super(source);
93 values = new double[getCapacity()];
94 System.arraycopy(source.values, 0, values, 0, getCapacity());
95 missingEntries = source.missingEntries;
96 }
97
98 /**
99 * Get the stored value associated with the given key
100 * @param key key associated with the data
101 * @return data associated with the key
102 */
103 public double get(final int key) {
104 final int index = locate(key);
105 return index < 0 ? missingEntries : values[index];
106 }
107
108 /**
109 * Get an iterator over map elements.
110 * <p>The specialized iterators returned are fail-fast: they throw a
111 * <code>ConcurrentModificationException</code> when they detect the map
112 * has been modified during iteration.</p>
113 * @return iterator over the map elements
114 */
115 public Iterator iterator() {
116 return new Iterator();
117 }
118
119 /** {@inheritDoc} */
120 @Override
121 public boolean equals(final Object o) {
122 if (this == o) {
123 return true;
124 }
125 if (o == null || getClass() != o.getClass()) {
126 return false;
127 }
128 final OpenIntToDoubleHashMap that = (OpenIntToDoubleHashMap) o;
129 return equalKeys(that) && equalStates(that) && Arrays.equals(values, that.values);
130 }
131
132 /** {@inheritDoc} */
133 @Override
134 public int hashCode() {
135 return keysStatesHashCode() + Arrays.hashCode(values);
136 }
137
138 /**
139 * Remove the value associated with a key.
140 * @param key key to which the value is associated
141 * @return removed value
142 */
143 public double remove(final int key) {
144 final int index = locate(key);
145 if (index < 0) {
146 return missingEntries;
147 } else {
148 final double previous = values[index];
149 doRemove(index);
150 values[index] = missingEntries;
151 return previous;
152 }
153 }
154
155 /**
156 * Put a value associated with a key in the map.
157 * @param key key to which value is associated
158 * @param value value to put in the map
159 * @return previous value associated with the key
160 */
161 public double put(final int key, final double value) {
162 final InsertionHolder ih = put(key);
163 final double previous = ih.isExisting() ? values[ih.getIndex()] : missingEntries;
164 values[ih.getIndex()] = value;
165 return previous;
166 }
167
168 /** {@inheritDoc} */
169 @Override
170 protected int growTable(final int oldIndex) {
171 final double[] newValues = new double[RESIZE_MULTIPLIER * values.length];
172 final int newIndex = doGrowTable(oldIndex, (src, dest) -> newValues[dest] = values[src]);
173 values = newValues;
174 return newIndex;
175 }
176
177 /** Iterator class for the map. */
178 public class Iterator extends BaseIterator {
179
180 /** Get the value of current entry.
181 * @return value of current entry
182 * @exception ConcurrentModificationException if the map is modified during iteration
183 * @exception NoSuchElementException if there is no element left in the map
184 */
185 public double value() throws ConcurrentModificationException, NoSuchElementException {
186 return values[getCurrent()];
187 }
188
189 }
190
191 /**
192 * Read a serialized object.
193 * @param stream input stream
194 * @throws IOException if object cannot be read
195 * @throws ClassNotFoundException if the class corresponding
196 * to the serialized object cannot be found
197 */
198 private void readObject(final ObjectInputStream stream)
199 throws IOException, ClassNotFoundException {
200 stream.defaultReadObject();
201 resetCount();
202 }
203
204 /**
205 * Replace the instance with a data transfer object for serialization.
206 * @return data transfer object that will be serialized
207 */
208 private Object writeReplace() {
209 return new DataTransferObject(missingEntries, getSize(), iterator());
210 }
211
212 /** Internal class used only for serialization. */
213 private static class DataTransferObject implements Serializable {
214
215 /** Serializable UID. */
216 private static final long serialVersionUID = 20240326L;
217
218 /** Return value for missing entries. */
219 private final double missingEntries;
220
221 /** Keys table. */
222 private final int[] keys;
223
224 /** Values table. */
225 private final double[] values;
226
227 /** Simple constructor.
228 * @param missingEntries return value for missing entries
229 * @param size number of objects in the map
230 * @param iterator iterator on serialized map
231 */
232 DataTransferObject(final double missingEntries, final int size, final Iterator iterator) {
233 this.missingEntries = missingEntries;
234 this.keys = new int[size];
235 this.values = new double[size];
236 for (int i = 0; i < size; ++i) {
237 iterator.advance();
238 keys[i] = iterator.key();
239 values[i] = iterator.value();
240 }
241 }
242
243 /** Replace the deserialized data transfer object with a {@link OpenIntToDoubleHashMap}.
244 * @return replacement {@link OpenIntToDoubleHashMap}
245 */
246 private Object readResolve() {
247 final OpenIntToDoubleHashMap map = new OpenIntToDoubleHashMap(missingEntries);
248 for (int i = 0; i < keys.length; ++i) {
249 map.put(keys[i], values[i]);
250 }
251 return map;
252 }
253
254 }
255
256 }