View Javadoc
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 }