Find the element that Appears Once - Using BitSet


We can use different approaches:
1. Use two hashsets(as we don't need the real count): uniqueSet, duplicateSet
The first time we see the element, we put it into both sets, if we see it again, we remove it from uniqueSet.
2. We can use BitSet to reduce space - uniqueBitSet and duplicateBitSet
The first time we see the element, we put set its bit in uniqueBitSet and duplicateBitSet, if we see it again, we clear its bit in duplicateBitSet.
    // Using Bitset
    public static int findUnqiueNumber1(final int[] array) {
        final BitSet unique = new BitSet(), duplicate = new BitSet();
        for (final int number : array) {
            // first time see this number
            if (!duplicate.get(number)) {
                unique.set(number);
                duplicate.set(number);
            } else {
                // if see it again
                unique.clear(number);
            }
        }

        final int unqinueValue = unique.nextSetBit(0);

        if (unqinueValue == -1) {
            throw new NoUniqueException("No unique number");
        }
        final int nextUnqiueValue = unique.nextSetBit(unqinueValue + 1);
        if (nextUnqiueValue != -1) {
            throw new MoreThanOneUniqueException(String.format(
                    "More than 1 unique number found. We found at least %s, %s", unqinueValue, nextUnqiueValue));
        }

        return unqinueValue;
    }

    // Using HashSet
    public static int findUnqiueNumber2(final int[] array) {
        final Set<Integer> uniqueSet = new HashSet<>(), duplicateSet = new HashSet<>();
        for (final int number : array) {
            // first time see this number
            if (!duplicateSet.contains(number)) {
                uniqueSet.add(number);
                duplicateSet.add(number);
            } else {
                // if see it again
                uniqueSet.remove(number);
            }
        }

        if (uniqueSet.isEmpty()) {
            throw new NoUniqueException("No unique number");
        }
        if (uniqueSet.size() > 1) {
            throw new MoreThanOneUniqueException(
                    String.format("More than 1 unique number found. We found %s count", uniqueSet.size()));
        }
        final int unqinueValue = uniqueSet.iterator().next();
        return unqinueValue;
    }


    private static class MoreThanOneUniqueException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        public MoreThanOneUniqueException(final String messge) {
            super(messge);
        }
    }
    private static class NoUniqueException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        public NoUniqueException(final String messge) {
            super(messge);
        }
    }

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void test() {
        Assert.assertEquals(findUnqiueNumber2(new int[] {1}), 1);
        Assert.assertEquals(findUnqiueNumber2(new int[] {1, 2, 2}), 1);
        Assert.assertEquals(findUnqiueNumber2(new int[] {1, 2, 2}), 1);
    }

    @Test
    public void testNoUnqiue() {
        thrown.expect(NoUniqueException.class);
        findUnqiueNumber2(new int[] {});
    }

    @Test
    public void testNoUnqiue2() {
        thrown.expect(NoUniqueException.class);
        findUnqiueNumber2(new int[] {1, 1, 2, 2});
    }

    @Test
    public void testMoreThanONeUnqiue() {
        thrown.expect(MoreThanOneUniqueException.class);
        findUnqiueNumber2(new int[] {1, 2});
    } 
Related:
[CareerCup] 10.4 Find All Duplicates Elements
You have an array with all the numbers from 1 to N, where N is at most 32,000. The array may have duplicate entries and you do not know what N is. With only 4 kilobytes of memory available, how would you print all duplicate elements in the array?

It can also be solved by using BitSet. add i into bitset, if i happens again, print it out.
Bonus:
org.apache.commons.lang3.ArrayUtils.removeElements(int[], int...)
    public static int[] removeElements(final int[] array, final int... values) {
        if (isEmpty(array) || isEmpty(values)) {
            return clone(array);
        }
        final HashMap<Integer, MutableInt> occurrences = new HashMap<Integer, MutableInt>(values.length); // use MutableInt to avoid Integer creation again and again.
        for (final int v : values) {
            final Integer boxed = Integer.valueOf(v);
            final MutableInt count = occurrences.get(boxed);
            if (count == null) {
                occurrences.put(boxed, new MutableInt(1));
            } else {
                count.increment();
            }
        }
        final BitSet toRemove = new BitSet();
        for (final Map.Entry<Integer, MutableInt> e : occurrences.entrySet()) {
            final Integer v = e.getKey();
            int found = 0;
            for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
                found = indexOf(array, v.intValue(), found);
                if (found < 0) {
                    break;
                }
                toRemove.set(found++);
            }
        }
        return (int[]) removeAll(array, toRemove);
    }
    static Object removeAll(final Object array, final BitSet indices) {
        final int srcLength = ArrayUtils.getLength(array);
        // No need to check maxIndex here, because method only currently called from removeElements()
        // which guarantee to generate on;y valid bit entries.
//        final int maxIndex = indices.length();
//        if (maxIndex > srcLength) { 
//            throw new IndexOutOfBoundsException("Index: " + (maxIndex-1) + ", Length: " + srcLength);
//        }
        final int removals = indices.cardinality(); // true bits are items to remove
        final Object result = Array.newInstance(array.getClass().getComponentType(), srcLength - removals);
        int srcIndex=0;
        int destIndex=0;
        int count;
        int set;
        while((set = indices.nextSetBit(srcIndex)) != -1){
            count = set - srcIndex;
            if (count > 0) {
               System.arraycopy(array, srcIndex, result, destIndex, count); // System.arraycopy is native and faster.
               destIndex += count;
            }
            srcIndex = indices.nextClearBit(set);
        }
        count = srcLength - srcIndex;
        if (count > 0) {
            System.arraycopy(array, srcIndex, result, destIndex, count);            
        }
        return result;
    }

java.util.BitSet.cardinality()
public int cardinality() {
    int sum = 0;
    for (int i = 0; i < wordsInUse; i++)
        sum += Long.bitCount(words[i]);
    return sum;
}    

Labels

adsense (5) Algorithm (69) Algorithm Series (35) Android (7) ANT (6) bat (8) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) Java (186) JavaScript (27) JSON (7) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) regex (5) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) xml (5)