jaulib v1.3.0
Jau Support Library (C++, Java, ..)
ArrayHashSet.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2010 Gothel Software e.K.
5 * Copyright (c) 2010 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27package org.jau.util;
28
29import java.util.ArrayList;
30import java.util.Collection;
31import java.util.HashMap;
32import java.util.Iterator;
33import java.util.List;
34import java.util.ListIterator;
35
36/**
37 * Hashed ArrayList implementation of the List and Collection interface.
38 *
39 * Implementation properties are:
40 * <ul>
41 * <li> Unique elements utilizing {@link java.lang.Object#hashCode()} for O(1) operations, see below.</li>
42 * <li> Provides {@link java.util.List} functionality,
43 * ie {@link java.util.List#indexOf(java.lang.Object)}
44 * and {@link java.util.List#get(int)}, hence object identity can be implemented.</li>
45 * <li> Object identity via {@link #get(java.lang.Object)}</li>
46 * <li> Java 1.5 compatible</li>
47 * </ul>
48 *
49 * O(1) operations:
50 * <ul>
51 * <li> adding new element(s) </li>
52 * <li> test for containment </li>
53 * <li> identity </li>
54 * <li> trying to remove non existent elements </li>
55 * </ul>
56 *
57 * O(n) operations:
58 * <ul>
59 * <li> removing existing elements</li>
60 * </ul>
61 *
62 * For thread safety, the application shall decorate access to instances via
63 * {@link jau.test.util.parallel.locks.RecursiveLock}.
64 *
65*/
66public class ArrayHashSet<E>
67 implements Cloneable, List<E>
68{
69 /**
70 * Default load factor: {@value}
71 */
72 public static final float DEFAULT_LOAD_FACTOR = 0.75f;
73 /**
74 * The default initial capacity: {@value}
75 */
76 public static final int DEFAULT_INITIAL_CAPACITY = 16;
77
78 private final HashMap<E,E> map; // object -> object
79 private final ArrayList<E> data; // list of objects
80 private final boolean supportNullValue;
81
82 /**
83 *
84 * @param supportNullValue Use {@code true} for default behavior, i.e. {@code null} can be a valid value.
85 * Use {@code false} if {@code null} is not a valid value,
86 * here {@link #remove(E)} and {@link #getOrAdd(Object)} will be optimized.
87 * @param initialCapacity use {@link #DEFAULT_INITIAL_CAPACITY} for default
88 * @param loadFactor use {@link #DEFAULT_LOAD_FACTOR} for default
89 * @see #supportsNullValue()
90 */
91 public ArrayHashSet(final boolean supportNullValue, final int initialCapacity, final float loadFactor) {
92 this.map = new HashMap<E,E>(initialCapacity, loadFactor);
93 this.data = new ArrayList<E>(initialCapacity);
94 this.supportNullValue = supportNullValue;
95 }
96
97 /**
98 * @return a shallow copy of this ArrayHashSet, elements are not copied.
99 */
101 map = new HashMap<E, E>(o.map);
102 data = new ArrayList<E>(o.data);
103 supportNullValue = o.supportNullValue;
104 }
105
106 /**
107 * Returns {@code true} for default behavior, i.e. {@code null} can be a valid value.
108 * <p>
109 * Returns {@code false} if {@code null} is not a valid value,
110 * here {@link #remove(E)} and {@link #getOrAdd(Object)} are optimized operations.
111 * </p>
112 * @see #ArrayHashSet(boolean, int, float)
113 */
114 public final boolean supportsNullValue() { return supportNullValue; }
115
116 //
117 // Cloneable
118 //
119
120 /**
121 * @return a shallow copy of this ArrayHashSet, elements are not copied.
122 */
123 @Override
124 public final Object clone() {
125 return new ArrayHashSet<E>(this);
126 }
127
128 /** Returns this object ordered ArrayList. Use w/ care, it's not a copy. */
129 public final ArrayList<E> getData() { return data; }
130 /** Returns this object hash map. Use w/ care, it's not a copy. */
131 public final HashMap<E,E> getMap() { return map; }
132
133 @Override
134 public final String toString() { return data.toString(); }
135
136 //
137 // Collection
138 //
139
140 @Override
141 public final void clear() {
142 data.clear();
143 map.clear();
144 }
145
146 /**
147 * Add element at the end of this list, if it is not contained yet.
148 * <br>
149 * This is an O(1) operation
150 * <p>
151 * {@inheritDoc}
152 * </p>
153 *
154 * @return true if the element was added to this list,
155 * otherwise false (already contained).
156 * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
157 */
158 @Override
159 public final boolean add(final E element) throws NullPointerException {
160 if( !supportNullValue ) {
161 checkNull(element);
162 }
163 if( !map.containsKey(element) ) {
164 // !exists
165 if(null != map.put(element, element)) {
166 // slips a valid null ..
167 throw new InternalError("Already existing, but checked before: "+element);
168 }
169 if(!data.add(element)) {
170 throw new InternalError("Couldn't add element: "+element);
171 }
172 return true;
173 }
174 return false;
175 }
176
177 /**
178 * Remove element from this list.
179 * <br>
180 * This is an O(1) operation, in case the element does not exist,
181 * otherwise O(n).
182 * <p>
183 * {@inheritDoc}
184 * </p>
185 *
186 * @return true if the element was removed from this list,
187 * otherwise false (not contained).
188 * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
189 */
190 @Override
191 public final boolean remove(final Object element) throws NullPointerException {
192 if( supportNullValue ) {
193 if( map.containsKey(element) ) {
194 // exists
195 map.remove(element);
196 if ( !data.remove(element) ) {
197 throw new InternalError("Couldn't remove prev mapped element: "+element);
198 }
199 return true;
200 }
201 } else {
202 checkNull(element);
203 if ( null != map.remove(element) ) {
204 // exists
205 if ( !data.remove(element) ) {
206 throw new InternalError("Couldn't remove prev mapped element: "+element);
207 }
208 return true;
209 }
210 }
211 return false;
212 }
213
214 /**
215 * Add all elements of given {@link java.util.Collection} at the end of this list.
216 * <br>
217 * This is an O(n) operation, over the given Collection size.
218 * <p>
219 * {@inheritDoc}
220 * </p>
221 *
222 * @return true if at least one element was added to this list,
223 * otherwise false (completely container).
224 */
225 @Override
226 public final boolean addAll(final Collection<? extends E> c) {
227 boolean mod = false;
228 for (final E o : c) {
229 mod |= add(o);
230 }
231 return mod;
232 }
233
234 /**
235 * Test for containment
236 * <br>
237 * This is an O(1) operation.
238 * <p>
239 * {@inheritDoc}
240 * </p>
241 *
242 * @return true if the given element is contained by this list using fast hash map,
243 * otherwise false.
244 */
245 @Override
246 public final boolean contains(final Object element) {
247 return map.containsKey(element);
248 }
249
250 /**
251 * Test for containment of given {@link java.util.Collection}
252 * <br>
253 * This is an O(n) operation, over the given Collection size.
254 * <p>
255 * {@inheritDoc}
256 * </p>
257 *
258 * @return true if the given Collection is completly contained by this list using hash map,
259 * otherwise false.
260 */
261 @Override
262 public final boolean containsAll(final Collection<?> c) {
263 for (final Object o : c) {
264 if (!this.contains(o)) {
265 return false;
266 }
267 }
268 return true;
269 }
270
271 /**
272 * Remove all elements of given {@link java.util.Collection} from this list.
273 * <br>
274 * This is an O(n) operation.
275 * <p>
276 * {@inheritDoc}
277 * </p>
278 *
279 * @return true if at least one element of this list was removed,
280 * otherwise false.
281 */
282 @Override
283 public final boolean removeAll(final Collection<?> c) {
284 boolean mod = false;
285 for (final Object o : c) {
286 mod |= this.remove(o);
287 }
288 return mod;
289 }
290
291 /**
292 * Retain all elements of the given {@link java.util.Collection} c, ie
293 * remove all elements not contained by the given {@link java.util.Collection} c.
294 * <br>
295 * This is an O(n) operation.
296 * <p>
297 * {@inheritDoc}
298 * </p>
299 *
300 * @return true if at least one element of this list was removed,
301 * otherwise false.
302 */
303 @Override
304 public final boolean retainAll(final Collection<?> c) {
305 boolean mod = false;
306 for (final Object o : c) {
307 if (!c.contains(o)) {
308 mod |= this.remove(o);
309 }
310 }
311 return mod;
312 }
313
314 /**
315 * This is an O(n) operation.
316 * <p>
317 * {@inheritDoc}
318 * </p>
319 *
320 * @return true if arrayHashSet is of type ArrayHashSet and all entries are equal
321 * Performance: arrayHashSet(1)
322 */
323 @Override
324 public final boolean equals(final Object arrayHashSet) {
325 if ( !(arrayHashSet instanceof ArrayHashSet) ) {
326 return false;
327 }
328 return data.equals(((ArrayHashSet<?>)arrayHashSet).data);
329 }
330
331 /**
332 * This is an O(n) operation over the size of this list.
333 * <p>
334 * {@inheritDoc}
335 * </p>
336 *
337 * @return the hash code of this list as define in {@link java.util.List#hashCode()},
338 * ie hashing all elements of this list.
339 */
340 @Override
341 public final int hashCode() {
342 return data.hashCode();
343 }
344
345 @Override
346 public final boolean isEmpty() {
347 return data.isEmpty();
348 }
349
350 @Override
351 public final Iterator<E> iterator() {
352 return data.iterator();
353 }
354
355 @Override
356 public final int size() {
357 return data.size();
358 }
359
360 @Override
361 public final Object[] toArray() {
362 return data.toArray();
363 }
364
365 @Override
366 public final <T> T[] toArray(final T[] a) {
367 return data.toArray(a);
368 }
369
370 //
371 // List
372 //
373
374 @Override
375 public final E get(final int index) {
376 return data.get(index);
377 }
378
379 @Override
380 public final int indexOf(final Object element) {
381 return data.indexOf(element);
382 }
383
384 /**
385 * Add element at the given index in this list, if it is not contained yet.
386 * <br>
387 * This is an O(1) operation
388 * <p>
389 * {@inheritDoc}
390 * </p>
391 *
392 * @throws IllegalArgumentException if the given element was already contained
393 * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
394 */
395 @Override
396 public final void add(final int index, final E element) throws IllegalArgumentException, NullPointerException {
397 if( !supportNullValue ) {
398 checkNull(element);
399 }
400 if ( map.containsKey(element) ) {
401 throw new IllegalArgumentException("Element "+element+" is already contained");
402 }
403 if(null != map.put(element, element)) {
404 // slips a valid null ..
405 throw new InternalError("Already existing, but checked before: "+element);
406 }
407 // !exists
408 data.add(index, element);
409 }
410
411 /**
412 * <p>
413 * {@inheritDoc}
414 * </p>
415 * @throws UnsupportedOperationException
416 */
417 @Override
418 public final boolean addAll(final int index, final Collection<? extends E> c) throws UnsupportedOperationException {
419 throw new UnsupportedOperationException("Not supported yet.");
420 }
421
422 /**
423 * <p>
424 * {@inheritDoc}
425 * </p>
426 */
427 @Override
428 public final E set(final int index, final E element) {
429 final E old = remove(index);
430 if(null!=old) {
431 add(index, element);
432 }
433 return old;
434 }
435
436 /**
437 * Remove element at given index from this list.
438 * <br>
439 * This is an O(n) operation.
440 * <p>
441 * {@inheritDoc}
442 * </p>
443 *
444 * @return the removed object
445 */
446 @Override
447 public final E remove(final int index) {
448 final E o = get(index);
449 if( null!=o && remove(o) ) {
450 return o;
451 }
452 return null;
453 }
454
455 /**
456 * Since this list is unique, equivalent to {@link #indexOf(java.lang.Object)}.
457 * <br>
458 * This is an O(n) operation.
459 * <p>
460 * {@inheritDoc}
461 * </p>
462 *
463 * @return index of element, or -1 if not found
464 */
465 @Override
466 public final int lastIndexOf(final Object o) {
467 return indexOf(o);
468 }
469
470 @Override
471 public final ListIterator<E> listIterator() {
472 return data.listIterator();
473 }
474
475 @Override
476 public final ListIterator<E> listIterator(final int index) {
477 return data.listIterator(index);
478 }
479
480 @Override
481 public final List<E> subList(final int fromIndex, final int toIndex) {
482 return data.subList(fromIndex, toIndex);
483 }
484
485 //
486 // ArrayHashSet
487 //
488
489 /**
490 * @return a shallow copy of this ArrayHashSet's ArrayList, elements are not copied.
491 */
492 public final ArrayList<E> toArrayList() {
493 return new ArrayList<E>(data);
494 }
495
496 /**
497 * Identity method allowing to get the identical object, using the internal hash map.
498 * <br>
499 * This is an O(1) operation.
500 *
501 * @param element hash source to find the identical Object within this list
502 * @return object from this list, identical to the given <code>key</code> hash code,
503 * or null if not contained
504 */
505 public final E get(final Object element) {
506 return map.get(element);
507 }
508
509 /**
510 * Identity method allowing to get the identical object, using the internal hash map.<br>
511 * If the <code>element</code> is not yet contained, add it.
512 * <br>
513 * This is an O(1) operation.
514 *
515 * @param element hash source to find the identical Object within this list
516 * @return object from this list, identical to the given <code>key</code> hash code,
517 * or add the given <code>key</code> and return it.
518 * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
519 */
520 public final E getOrAdd(final E element) throws NullPointerException {
521 if( supportNullValue ) {
522 if( map.containsKey(element) ) {
523 // existent
524 return map.get(element);
525 }
526 } else {
527 checkNull(element);
528 final E identity = map.get(element);
529 if(null != identity) {
530 // existent
531 return identity;
532 }
533 }
534 // !existent
535 if(!this.add(element)) {
536 throw new InternalError("Element not mapped, but contained in list: "+element);
537 }
538 return element;
539 }
540
541 /**
542 * Test for containment
543 * <br>
544 * This is an O(n) operation, using equals operation over the list.
545 * <br>
546 * You may utilize this method to verify your hash values,<br>
547 * ie {@link #contains(java.lang.Object)} and {@link #containsSafe(java.lang.Object)}
548 * shall have the same result.<br>
549 *
550 * @return true if the given element is contained by this list using slow equals operation,
551 * otherwise false.
552 */
553 public final boolean containsSafe(final Object element) {
554 return data.contains(element);
555 }
556
557 private static final void checkNull(final Object element) throws NullPointerException {
558 if( null == element ) {
559 throw new NullPointerException("Null element not supported");
560 }
561 }
562}
Hashed ArrayList implementation of the List and Collection interface.
final boolean equals(final Object arrayHashSet)
This is an O(n) operation.
final Object[] toArray()
final Iterator< E > iterator()
final ListIterator< E > listIterator(final int index)
static final float DEFAULT_LOAD_FACTOR
Default load factor: {@value}.
final ListIterator< E > listIterator()
final boolean addAll(final Collection<? extends E > c)
Add all elements of given java.util.Collection at the end of this list.
final boolean containsAll(final Collection<?> c)
Test for containment of given java.util.Collection This is an O(n) operation, over the given Collec...
final int indexOf(final Object element)
final ArrayList< E > toArrayList()
final< T > T[] toArray(final T[] a)
final HashMap< E, E > getMap()
Returns this object hash map.
final boolean removeAll(final Collection<?> c)
Remove all elements of given java.util.Collection from this list.
final boolean add(final E element)
Add element at the end of this list, if it is not contained yet.
static final int DEFAULT_INITIAL_CAPACITY
The default initial capacity: {@value}.
ArrayHashSet(final boolean supportNullValue, final int initialCapacity, final float loadFactor)
final ArrayList< E > getData()
Returns this object ordered ArrayList.
final int lastIndexOf(final Object o)
Since this list is unique, equivalent to indexOf(java.lang.Object).
final boolean addAll(final int index, final Collection<? extends E > c)
final List< E > subList(final int fromIndex, final int toIndex)
final void add(final int index, final E element)
Add element at the given index in this list, if it is not contained yet.
final boolean supportsNullValue()
Returns true for default behavior, i.e.
final int hashCode()
This is an O(n) operation over the size of this list.
ArrayHashSet(final ArrayHashSet< E > o)
final boolean containsSafe(final Object element)
Test for containment This is an O(n) operation, using equals operation over the list.
final E getOrAdd(final E element)
Identity method allowing to get the identical object, using the internal hash map.
final boolean retainAll(final Collection<?> c)
Retain all elements of the given java.util.Collection c, ie remove all elements not contained by the ...
final boolean contains(final Object element)
Test for containment This is an O(1) operation.