JumboEnumSet.java revision dbd5f6a921c3f504b7626030babd0d6856b1275e
1/*
2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.util;
27
28/**
29 * Private implementation class for EnumSet, for "jumbo" enum types
30 * (i.e., those with more than 64 elements).
31 *
32 * @author Josh Bloch
33 * @since 1.5
34 * @serial exclude
35 */
36class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
37    private static final long serialVersionUID = 334349849919042784L;
38
39    /**
40     * Bit vector representation of this set.  The ith bit of the jth
41     * element of this array represents the  presence of universe[64*j +i]
42     * in this set.
43     */
44    private long elements[];
45
46    // Redundant - maintained for performance
47    private int size = 0;
48
49    JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
50        super(elementType, universe);
51        elements = new long[(universe.length + 63) >>> 6];
52    }
53
54    void addRange(E from, E to) {
55        int fromIndex = from.ordinal() >>> 6;
56        int toIndex = to.ordinal() >>> 6;
57
58        if (fromIndex == toIndex) {
59            elements[fromIndex] = (-1L >>>  (from.ordinal() - to.ordinal() - 1))
60                            << from.ordinal();
61        } else {
62            elements[fromIndex] = (-1L << from.ordinal());
63            for (int i = fromIndex + 1; i < toIndex; i++)
64                elements[i] = -1;
65            elements[toIndex] = -1L >>> (63 - to.ordinal());
66        }
67        size = to.ordinal() - from.ordinal() + 1;
68    }
69
70    void addAll() {
71        for (int i = 0; i < elements.length; i++)
72            elements[i] = -1;
73        elements[elements.length - 1] >>>= -universe.length;
74        size = universe.length;
75    }
76
77    void complement() {
78        for (int i = 0; i < elements.length; i++)
79            elements[i] = ~elements[i];
80        elements[elements.length - 1] &= (-1L >>> -universe.length);
81        size = universe.length - size;
82    }
83
84    /**
85     * Returns an iterator over the elements contained in this set.  The
86     * iterator traverses the elements in their <i>natural order</i> (which is
87     * the order in which the enum constants are declared). The returned
88     * Iterator is a "weakly consistent" iterator that will never throw {@link
89     * ConcurrentModificationException}.
90     *
91     * @return an iterator over the elements contained in this set
92     */
93    public Iterator<E> iterator() {
94        return new EnumSetIterator<>();
95    }
96
97    private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
98        /**
99         * A bit vector representing the elements in the current "word"
100         * of the set not yet returned by this iterator.
101         */
102        long unseen;
103
104        /**
105         * The index corresponding to unseen in the elements array.
106         */
107        int unseenIndex = 0;
108
109        /**
110         * The bit representing the last element returned by this iterator
111         * but not removed, or zero if no such element exists.
112         */
113        long lastReturned = 0;
114
115        /**
116         * The index corresponding to lastReturned in the elements array.
117         */
118        int lastReturnedIndex = 0;
119
120        EnumSetIterator() {
121            unseen = elements[0];
122        }
123
124        @Override
125        public boolean hasNext() {
126            while (unseen == 0 && unseenIndex < elements.length - 1)
127                unseen = elements[++unseenIndex];
128            return unseen != 0;
129        }
130
131        @Override
132        @SuppressWarnings("unchecked")
133        public E next() {
134            if (!hasNext())
135                throw new NoSuchElementException();
136            lastReturned = unseen & -unseen;
137            lastReturnedIndex = unseenIndex;
138            unseen -= lastReturned;
139            return (E) universe[(lastReturnedIndex << 6)
140                                + Long.numberOfTrailingZeros(lastReturned)];
141        }
142
143        @Override
144        public void remove() {
145            if (lastReturned == 0)
146                throw new IllegalStateException();
147            final long oldElements = elements[lastReturnedIndex];
148            elements[lastReturnedIndex] &= ~lastReturned;
149            if (oldElements != elements[lastReturnedIndex]) {
150                size--;
151            }
152            lastReturned = 0;
153        }
154    }
155
156    /**
157     * Returns the number of elements in this set.
158     *
159     * @return the number of elements in this set
160     */
161    public int size() {
162        return size;
163    }
164
165    /**
166     * Returns <tt>true</tt> if this set contains no elements.
167     *
168     * @return <tt>true</tt> if this set contains no elements
169     */
170    public boolean isEmpty() {
171        return size == 0;
172    }
173
174    /**
175     * Returns <tt>true</tt> if this set contains the specified element.
176     *
177     * @param e element to be checked for containment in this collection
178     * @return <tt>true</tt> if this set contains the specified element
179     */
180    public boolean contains(Object e) {
181        if (e == null)
182            return false;
183        Class<?> eClass = e.getClass();
184        if (eClass != elementType && eClass.getSuperclass() != elementType)
185            return false;
186
187        int eOrdinal = ((Enum<?>)e).ordinal();
188        return (elements[eOrdinal >>> 6] & (1L << eOrdinal)) != 0;
189    }
190
191    // Modification Operations
192
193    /**
194     * Adds the specified element to this set if it is not already present.
195     *
196     * @param e element to be added to this set
197     * @return <tt>true</tt> if the set changed as a result of the call
198     *
199     * @throws NullPointerException if <tt>e</tt> is null
200     */
201    public boolean add(E e) {
202        typeCheck(e);
203
204        int eOrdinal = e.ordinal();
205        int eWordNum = eOrdinal >>> 6;
206
207        long oldElements = elements[eWordNum];
208        elements[eWordNum] |= (1L << eOrdinal);
209        boolean result = (elements[eWordNum] != oldElements);
210        if (result)
211            size++;
212        return result;
213    }
214
215    /**
216     * Removes the specified element from this set if it is present.
217     *
218     * @param e element to be removed from this set, if present
219     * @return <tt>true</tt> if the set contained the specified element
220     */
221    public boolean remove(Object e) {
222        if (e == null)
223            return false;
224        Class<?> eClass = e.getClass();
225        if (eClass != elementType && eClass.getSuperclass() != elementType)
226            return false;
227        int eOrdinal = ((Enum<?>)e).ordinal();
228        int eWordNum = eOrdinal >>> 6;
229
230        long oldElements = elements[eWordNum];
231        elements[eWordNum] &= ~(1L << eOrdinal);
232        boolean result = (elements[eWordNum] != oldElements);
233        if (result)
234            size--;
235        return result;
236    }
237
238    // Bulk Operations
239
240    /**
241     * Returns <tt>true</tt> if this set contains all of the elements
242     * in the specified collection.
243     *
244     * @param c collection to be checked for containment in this set
245     * @return <tt>true</tt> if this set contains all of the elements
246     *        in the specified collection
247     * @throws NullPointerException if the specified collection is null
248     */
249    public boolean containsAll(Collection<?> c) {
250        if (!(c instanceof JumboEnumSet))
251            return super.containsAll(c);
252
253        JumboEnumSet<?> es = (JumboEnumSet<?>)c;
254        if (es.elementType != elementType)
255            return es.isEmpty();
256
257        for (int i = 0; i < elements.length; i++)
258            if ((es.elements[i] & ~elements[i]) != 0)
259                return false;
260        return true;
261    }
262
263    /**
264     * Adds all of the elements in the specified collection to this set.
265     *
266     * @param c collection whose elements are to be added to this set
267     * @return <tt>true</tt> if this set changed as a result of the call
268     * @throws NullPointerException if the specified collection or any of
269     *     its elements are null
270     */
271    public boolean addAll(Collection<? extends E> c) {
272        if (!(c instanceof JumboEnumSet))
273            return super.addAll(c);
274
275        JumboEnumSet<?> es = (JumboEnumSet<?>)c;
276        if (es.elementType != elementType) {
277            if (es.isEmpty())
278                return false;
279            else
280                throw new ClassCastException(
281                    es.elementType + " != " + elementType);
282        }
283
284        for (int i = 0; i < elements.length; i++)
285            elements[i] |= es.elements[i];
286        return recalculateSize();
287    }
288
289    /**
290     * Removes from this set all of its elements that are contained in
291     * the specified collection.
292     *
293     * @param c elements to be removed from this set
294     * @return <tt>true</tt> if this set changed as a result of the call
295     * @throws NullPointerException if the specified collection is null
296     */
297    public boolean removeAll(Collection<?> c) {
298        if (!(c instanceof JumboEnumSet))
299            return super.removeAll(c);
300
301        JumboEnumSet<?> es = (JumboEnumSet<?>)c;
302        if (es.elementType != elementType)
303            return false;
304
305        for (int i = 0; i < elements.length; i++)
306            elements[i] &= ~es.elements[i];
307        return recalculateSize();
308    }
309
310    /**
311     * Retains only the elements in this set that are contained in the
312     * specified collection.
313     *
314     * @param c elements to be retained in this set
315     * @return <tt>true</tt> if this set changed as a result of the call
316     * @throws NullPointerException if the specified collection is null
317     */
318    public boolean retainAll(Collection<?> c) {
319        if (!(c instanceof JumboEnumSet))
320            return super.retainAll(c);
321
322        JumboEnumSet<?> es = (JumboEnumSet<?>)c;
323        if (es.elementType != elementType) {
324            boolean changed = (size != 0);
325            clear();
326            return changed;
327        }
328
329        for (int i = 0; i < elements.length; i++)
330            elements[i] &= es.elements[i];
331        return recalculateSize();
332    }
333
334    /**
335     * Removes all of the elements from this set.
336     */
337    public void clear() {
338        Arrays.fill(elements, 0);
339        size = 0;
340    }
341
342    /**
343     * Compares the specified object with this set for equality.  Returns
344     * <tt>true</tt> if the given object is also a set, the two sets have
345     * the same size, and every member of the given set is contained in
346     * this set.
347     *
348     * @param o object to be compared for equality with this set
349     * @return <tt>true</tt> if the specified object is equal to this set
350     */
351    public boolean equals(Object o) {
352        if (!(o instanceof JumboEnumSet))
353            return super.equals(o);
354
355        JumboEnumSet<?> es = (JumboEnumSet<?>)o;
356        if (es.elementType != elementType)
357            return size == 0 && es.size == 0;
358
359        return Arrays.equals(es.elements, elements);
360    }
361
362    /**
363     * Recalculates the size of the set.  Returns true if it's changed.
364     */
365    private boolean recalculateSize() {
366        int oldSize = size;
367        size = 0;
368        for (long elt : elements)
369            size += Long.bitCount(elt);
370
371        return size != oldSize;
372    }
373
374    public EnumSet<E> clone() {
375        JumboEnumSet<E> result = (JumboEnumSet<E>) super.clone();
376        result.elements = result.elements.clone();
377        return result;
378    }
379}
380