Libraryless. Click here for Pure Java version (365L/3K).
1 | sclass IntIntHashMap is IIntIntMap { |
2 | private static final int FREE_KEY = 0; |
3 | |
4 | public static final int NO_VALUE = 0; |
5 | |
6 | /** Keys and values */ |
7 | private int[] m_data; |
8 | |
9 | /** Do we have 'free' key in the map? */ |
10 | private boolean m_hasFreeKey; |
11 | /** Value of 'free' key */ |
12 | private int m_freeValue; |
13 | |
14 | /** Fill factor, must be between (0 and 1) */ |
15 | private final float m_fillFactor; |
16 | /** We will resize a map once it reaches this size */ |
17 | private int m_threshold; |
18 | /** Current map size */ |
19 | private int m_size; |
20 | |
21 | /** Mask to calculate the original position */ |
22 | private int m_mask; |
23 | private int m_mask2; |
24 | |
25 | *( final int size, final float fillFactor ) |
26 | { |
27 | if ( fillFactor <= 0 || fillFactor >= 1 ) |
28 | throw new IllegalArgumentException( "FillFactor must be in (0, 1)" ); |
29 | if ( size <= 0 ) |
30 | throw new IllegalArgumentException( "Size must be positive!" ); |
31 | final int capacity = arraySize(size, fillFactor); |
32 | m_mask = capacity - 1; |
33 | m_mask2 = capacity*2 - 1; |
34 | m_fillFactor = fillFactor; |
35 | |
36 | m_data = new int[capacity * 2]; |
37 | m_threshold = (int) (capacity * fillFactor); |
38 | } |
39 | |
40 | public int get( final int key ) |
41 | { |
42 | int ptr = ( phiMix( key ) & m_mask) << 1; |
43 | |
44 | if ( key == FREE_KEY ) |
45 | return m_hasFreeKey ? m_freeValue : NO_VALUE; |
46 | |
47 | int k = m_data[ ptr ]; |
48 | |
49 | if ( k == FREE_KEY ) |
50 | return NO_VALUE; //end of chain already |
51 | if ( k == key ) //we check FREE prior to this call |
52 | return m_data[ ptr + 1 ]; |
53 | |
54 | while ( true ) |
55 | { |
56 | ptr = (ptr + 2) & m_mask2; //that's next index |
57 | k = m_data[ ptr ]; |
58 | if ( k == FREE_KEY ) |
59 | return NO_VALUE; |
60 | if ( k == key ) |
61 | return m_data[ ptr + 1 ]; |
62 | } |
63 | } |
64 | |
65 | public int put( final int key, final int value ) |
66 | { |
67 | if ( key == FREE_KEY ) |
68 | { |
69 | final int ret = m_freeValue; |
70 | if ( !m_hasFreeKey ) |
71 | ++m_size; |
72 | m_hasFreeKey = true; |
73 | m_freeValue = value; |
74 | return ret; |
75 | } |
76 | |
77 | int ptr = ( phiMix( key ) & m_mask) << 1; |
78 | int k = m_data[ptr]; |
79 | if ( k == FREE_KEY ) //end of chain already |
80 | { |
81 | m_data[ ptr ] = key; |
82 | m_data[ ptr + 1 ] = value; |
83 | if ( m_size >= m_threshold ) |
84 | rehash( m_data.length * 2 ); //size is set inside |
85 | else |
86 | ++m_size; |
87 | return NO_VALUE; |
88 | } |
89 | else if ( k == key ) //we check FREE prior to this call |
90 | { |
91 | final int _ret = m_data[ ptr + 1 ]; |
92 | m_data[ ptr + 1 ] = value; |
93 | return _ret; |
94 | } |
95 | |
96 | while ( true ) |
97 | { |
98 | ptr = ( ptr + 2 ) & m_mask2; //that's next index calculation |
99 | k = m_data[ ptr ]; |
100 | if ( k == FREE_KEY ) |
101 | { |
102 | m_data[ ptr ] = key; |
103 | m_data[ ptr + 1 ] = value; |
104 | if ( m_size >= m_threshold ) |
105 | rehash( m_data.length * 2 ); //size is set inside |
106 | else |
107 | ++m_size; |
108 | return NO_VALUE; |
109 | } |
110 | else if ( k == key ) |
111 | { |
112 | final int _ret = m_data[ ptr + 1 ]; |
113 | m_data[ ptr + 1 ] = value; |
114 | return _ret; |
115 | } |
116 | } |
117 | } |
118 | |
119 | public int remove( final int key ) |
120 | { |
121 | if ( key == FREE_KEY ) |
122 | { |
123 | if ( !m_hasFreeKey ) |
124 | return NO_VALUE; |
125 | m_hasFreeKey = false; |
126 | --m_size; |
127 | return m_freeValue; //value is not cleaned |
128 | } |
129 | |
130 | int ptr = ( phiMix( key ) & m_mask) << 1; |
131 | int k = m_data[ ptr ]; |
132 | if ( k == key ) //we check FREE prior to this call |
133 | { |
134 | final int res = m_data[ ptr + 1 ]; |
135 | shiftKeys( ptr ); |
136 | --m_size; |
137 | return res; |
138 | } |
139 | else if ( k == FREE_KEY ) |
140 | return NO_VALUE; //end of chain already |
141 | while ( true ) |
142 | { |
143 | ptr = ( ptr + 2 ) & m_mask2; //that's next index calculation |
144 | k = m_data[ ptr ]; |
145 | if ( k == key ) |
146 | { |
147 | final int res = m_data[ ptr + 1 ]; |
148 | shiftKeys( ptr ); |
149 | --m_size; |
150 | return res; |
151 | } |
152 | else if ( k == FREE_KEY ) |
153 | return NO_VALUE; |
154 | } |
155 | } |
156 | |
157 | private int shiftKeys(int pos) |
158 | { |
159 | // Shift entries with the same hash. |
160 | int last, slot; |
161 | int k; |
162 | final int[] data = this.m_data; |
163 | while ( true ) |
164 | { |
165 | pos = ((last = pos) + 2) & m_mask2; |
166 | while ( true ) |
167 | { |
168 | if ((k = data[pos]) == FREE_KEY) |
169 | { |
170 | data[last] = FREE_KEY; |
171 | return last; |
172 | } |
173 | slot = ( phiMix( k ) & m_mask) << 1; //calculate the starting slot for the current key |
174 | if (last <= pos ? last >= slot || slot > pos : last >= slot && slot > pos) break; |
175 | pos = (pos + 2) & m_mask2; //go to the next entry |
176 | } |
177 | data[last] = k; |
178 | data[last + 1] = data[pos + 1]; |
179 | } |
180 | } |
181 | |
182 | |
183 | public int size() |
184 | { |
185 | return m_size; |
186 | } |
187 | |
188 | private void rehash( final int newCapacity ) |
189 | { |
190 | m_threshold = (int) (newCapacity/2 * m_fillFactor); |
191 | m_mask = newCapacity/2 - 1; |
192 | m_mask2 = newCapacity - 1; |
193 | |
194 | final int oldCapacity = m_data.length; |
195 | final int[] oldData = m_data; |
196 | |
197 | m_data = new int[ newCapacity ]; |
198 | m_size = m_hasFreeKey ? 1 : 0; |
199 | |
200 | for ( int i = 0; i < oldCapacity; i += 2 ) { |
201 | final int oldKey = oldData[ i ]; |
202 | if( oldKey != FREE_KEY ) |
203 | put( oldKey, oldData[ i + 1 ]); |
204 | } |
205 | } |
206 | |
207 | static int arraySize( final int expected, final float f ) { |
208 | final long s = Math.max( 2, nextPowerOfTwo( (long)Math.ceil( expected / f ) ) ); |
209 | if ( s > (1 << 30) ) throw new IllegalArgumentException( "Too large (" + expected + " expected elements with load factor " + f + ")" ); |
210 | return (int)s; |
211 | } |
212 | |
213 | static final int INT_PHI = 0x9E3779B9; |
214 | |
215 | static int phiMix( final int x ) { |
216 | final int h = x * INT_PHI; |
217 | return h ^ (h >> 16); |
218 | } |
219 | } |
Began life as a copy of #1026144
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): elmgxqgtpvxh, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1036017 |
Snippet name: | IntIntHashMap - by Mikhail Vorontsov, originally called IntIntMap4a |
Eternal ID of this version: | #1036017/1 |
Text MD5: | 8ce9a3657d46ca0b27d0e8fb60246609 |
Transpilation MD5: | afb8a8ab383f2e8ef2700f711f6a1f1f |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-08-30 18:35:36 |
Source code size: | 6260 bytes / 219 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 128 / 187 |
Referenced in: | [show references] |