/* * 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()"); } } } }