alluxio.collections.NonUniqueFieldIndex.java Source code

Java tutorial

Introduction

Here is the source code for alluxio.collections.NonUniqueFieldIndex.java

Source

/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.collections;

import com.google.common.collect.Iterables;
import io.netty.util.internal.chmv8.ConcurrentHashMapV8;

import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

import javax.annotation.concurrent.ThreadSafe;

/**
 * A class representing a non-unique index. A non-unique index is
 * an index where an index value can map to one or more objects.
 *
 * @param <T> type of objects in this index
 */
@ThreadSafe
public class NonUniqueFieldIndex<T> implements FieldIndex<T> {
    private final IndexDefinition<T> mIndexDefinition;
    private final ConcurrentHashMapV8<Object, ConcurrentHashSet<T>> mIndexMap;

    /**
     * Constructs a new {@link NonUniqueFieldIndex} instance.
     *
     * @param indexDefinition definition of index
     */
    public NonUniqueFieldIndex(IndexDefinition<T> indexDefinition) {
        mIndexMap = new ConcurrentHashMapV8<>(8, 0.95f, 8);
        mIndexDefinition = indexDefinition;
    }

    @Override
    public boolean add(T object) {
        Object fieldValue = mIndexDefinition.getFieldValue(object);

        ConcurrentHashSet<T> objSet;

        while (true) {
            objSet = mIndexMap.get(fieldValue);
            // If there is no object set for the current value, creates a new one.
            while (objSet == null) {
                mIndexMap.putIfAbsent(fieldValue, new ConcurrentHashSet<T>());
                objSet = mIndexMap.get(fieldValue);
            }

            synchronized (objSet) {
                if (objSet != mIndexMap.get(fieldValue)) {
                    continue;
                }
                // Adds the value to the object set.
                objSet.add(object);
                break;
            }
        }
        return true;
    }

    @Override
    public boolean remove(T object) {
        boolean res = false;
        Object fieldValue = mIndexDefinition.getFieldValue(object);
        ConcurrentHashSet<T> objSet = mIndexMap.get(fieldValue);
        if (objSet != null) {
            synchronized (objSet) {
                if (objSet != mIndexMap.get(fieldValue)) {
                    return false;
                }
                res = objSet.remove(object);
                if (objSet.isEmpty()) {
                    mIndexMap.remove(fieldValue, objSet);
                }
            }
        }
        return res;
    }

    @Override
    public void clear() {
        mIndexMap.clear();
    }

    @Override
    public boolean containsField(Object fieldValue) {
        return mIndexMap.containsKey(fieldValue);
    }

    @Override
    public boolean containsObject(T object) {
        Object fieldValue = mIndexDefinition.getFieldValue(object);
        ConcurrentHashSet<T> set = mIndexMap.get(fieldValue);

        if (set == null) {
            return false;
        }
        return set.contains(object);
    }

    @Override
    public Set<T> getByField(Object value) {
        Set<T> set = mIndexMap.get(value);
        return set == null ? Collections.<T>emptySet() : set;
    }

    @Override
    public T getFirst(Object value) {
        Set<T> all = mIndexMap.get(value);
        return all == null ? null : Iterables.getFirst(all, null);
    }

    @Override
    public Iterator<T> iterator() {
        return new NonUniqueFieldIndexIterator();
    }

    @Override
    public int size() {
        int totalSize = 0;
        for (ConcurrentHashSet<T> innerSet : mIndexMap.values()) {
            totalSize += innerSet.size();
        }
        return totalSize;
    }

    /**
     * Specialized iterator for {@link NonUniqueFieldIndex}.
     *
     * This is needed to support consistent removal from the set and the indices.
     */
    private class NonUniqueFieldIndexIterator implements Iterator<T> {
        /**
         * Iterator of {@link NonUniqueFieldIndex#mIndexMap}. This iterator keeps track of the
         * inner set which is under iteration.
         */
        private final Iterator<ConcurrentHashSet<T>> mIndexIterator;
        /**
         * Iterator inside each inner set. This iterator keeps track of the objects.
         */
        private Iterator<T> mObjectIterator;
        /**
         * Keeps track of current object. It is used to do the remove.
         */
        private T mObject;

        public NonUniqueFieldIndexIterator() {
            mIndexIterator = mIndexMap.values().iterator();
            mObjectIterator = null;
            mObject = null;
        }

        @Override
        public boolean hasNext() {
            if (mObjectIterator != null && mObjectIterator.hasNext()) {
                return true;
            }
            while (mIndexIterator.hasNext()) {
                mObjectIterator = mIndexIterator.next().iterator();
                if (mObjectIterator.hasNext()) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public T next() {
            while (mObjectIterator == null || !mObjectIterator.hasNext()) {
                mObjectIterator = mIndexIterator.next().iterator();
            }

            final T next = mObjectIterator.next();
            mObject = next;
            return next;
        }

        @Override
        public void remove() {
            if (mObject != null) {
                NonUniqueFieldIndex.this.remove(mObject);
                mObject = null;
            } else {
                throw new IllegalStateException("next() was not called before remove()");
            }
        }
    }
}