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  package org.hipparchus.util;
23  
24  import java.io.IOException;
25  import java.io.ObjectInputStream;
26  import java.io.Serializable;
27  import java.lang.reflect.Array;
28  import java.util.Arrays;
29  import java.util.ConcurrentModificationException;
30  import java.util.NoSuchElementException;
31  
32  import org.hipparchus.Field;
33  import org.hipparchus.FieldElement;
34  
35  /**
36   * Open addressed map from int to FieldElement.
37   * <p>This class provides a dedicated map from integers to FieldElements with a
38   * much smaller memory overhead than standard <code>java.util.Map</code>.</p>
39   * <p>This class is not synchronized. The specialized iterators returned by
40   * {@link #iterator()} are fail-fast: they throw a
41   * <code>ConcurrentModificationException</code> when they detect the map has been
42   * modified during iteration.</p>
43   * @param <T> the type of the field elements
44   */
45  public class OpenIntToFieldHashMap<T extends FieldElement<T>> extends AbstractOpenIntHashMap implements Serializable {
46  
47      /** Serializable version identifier. */
48      private static final long serialVersionUID = 20240326L;
49  
50      /** Field to which the elements belong. */
51      private final Field<T> field;
52  
53      /** Values table. */
54      private T[] values;
55  
56      /** Return value for missing entries. */
57      private final T missingEntries;
58  
59      /**
60       * Build an empty map with default size and using zero for missing entries.
61       * @param field field to which the elements belong
62       */
63      public OpenIntToFieldHashMap(final Field<T>field) {
64          this(field, DEFAULT_EXPECTED_SIZE, field.getZero());
65      }
66  
67      /**
68       * Build an empty map with default size
69       * @param field field to which the elements belong
70       * @param missingEntries value to return when a missing entry is fetched
71       */
72      public OpenIntToFieldHashMap(final Field<T>field, final T missingEntries) {
73          this(field, DEFAULT_EXPECTED_SIZE, missingEntries);
74      }
75  
76      /**
77       * Build an empty map with specified size and using zero for missing entries.
78       * @param field field to which the elements belong
79       * @param expectedSize expected number of elements in the map
80       */
81      public OpenIntToFieldHashMap(final Field<T> field,final int expectedSize) {
82          this(field, expectedSize, field.getZero());
83      }
84  
85      /**
86       * Build an empty map with specified size.
87       * @param field field to which the elements belong
88       * @param expectedSize expected number of elements in the map
89       * @param missingEntries value to return when a missing entry is fetched
90       */
91      public OpenIntToFieldHashMap(final Field<T> field, final int expectedSize,
92                                    final T missingEntries) {
93          super(expectedSize);
94          this.field          = field;
95          this.values         = buildArray(getCapacity());
96          this.missingEntries = missingEntries;
97      }
98  
99      /**
100      * Copy constructor.
101      * @param source map to copy
102      */
103     public OpenIntToFieldHashMap(final OpenIntToFieldHashMap<T> source) {
104         super(source);
105         field = source.field;
106         values = buildArray(getCapacity());
107         System.arraycopy(source.values, 0, values, 0, getCapacity());
108         missingEntries = source.missingEntries;
109     }
110 
111     /**
112      * Get the stored value associated with the given key
113      * @param key key associated with the data
114      * @return data associated with the key
115      */
116     public T get(final int key) {
117         final int index = locate(key);
118         return index < 0 ? missingEntries : values[index];
119     }
120 
121     /**
122      * Get an iterator over map elements.
123      * <p>The specialized iterators returned are fail-fast: they throw a
124      * <code>ConcurrentModificationException</code> when they detect the map
125      * has been modified during iteration.</p>
126      * @return iterator over the map elements
127      */
128     public Iterator iterator() {
129         return new Iterator();
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public boolean equals(final Object o) {
135         if (this == o) {
136             return true;
137         }
138         if (o == null || getClass() != o.getClass()) {
139             return false;
140         }
141         @SuppressWarnings("unchecked")
142         final OpenIntToFieldHashMap<T> that = (OpenIntToFieldHashMap<T>) o;
143         return equalKeys(that) &&
144                equalStates(that) &&
145                field.equals(that.field) &&
146                Arrays.equals(values, that.values);
147     }
148 
149     /** {@inheritDoc} */
150     @Override
151     public int hashCode() {
152         return keysStatesHashCode() + 67 * field.hashCode() + Arrays.hashCode(values);
153     }
154 
155     /**
156      * Remove the value associated with a key.
157      * @param key key to which the value is associated
158      * @return removed value
159      */
160     public T remove(final int key) {
161         final int index = locate(key);
162         if (index < 0) {
163             return missingEntries;
164         } else {
165             final T previous = values[index];
166             doRemove(index);
167             values[index] = missingEntries;
168             return previous;
169         }
170     }
171 
172     /**
173      * Put a value associated with a key in the map.
174      * @param key key to which value is associated
175      * @param value value to put in the map
176      * @return previous value associated with the key
177      */
178     public T put(final int key, final T value) {
179         final InsertionHolder ih = put(key);
180         final T previous = ih.isExisting() ? values[ih.getIndex()] : missingEntries;
181         values[ih.getIndex()] = value;
182         return previous;
183     }
184 
185     /**
186      * Grow the tables.
187      */
188     @Override
189     protected int growTable(final int oldIndex) {
190         final T[] newValues = buildArray(RESIZE_MULTIPLIER * values.length);
191         final int newIndex  = doGrowTable(oldIndex, (src, dest) -> newValues[dest] = values[src]);
192         values = newValues;
193         return newIndex;
194     }
195 
196     /** Iterator class for the map. */
197     public class Iterator extends BaseIterator {
198 
199         /** Get the value of current entry.
200          * @return value of current entry
201          * @exception ConcurrentModificationException if the map is modified during iteration
202          * @exception NoSuchElementException if there is no element left in the map
203          */
204         public T value() throws ConcurrentModificationException, NoSuchElementException {
205             return values[getCurrent()];
206         }
207 
208     }
209 
210     /**
211      * Read a serialized object.
212      * @param stream input stream
213      * @throws IOException if object cannot be read
214      * @throws ClassNotFoundException if the class corresponding
215      * to the serialized object cannot be found
216      */
217     private void readObject(final ObjectInputStream stream)
218         throws IOException, ClassNotFoundException {
219         stream.defaultReadObject();
220         resetCount();
221     }
222 
223     /** Build an array of elements.
224      * @param length size of the array to build
225      * @return a new array
226      */
227     @SuppressWarnings("unchecked") // field is of type T
228     private T[] buildArray(final int length) {
229         return (T[]) Array.newInstance(field.getRuntimeClass(), length);
230     }
231 
232 }