/**
* Copyright 2014 Yahoo! Inc. Licensed under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
* or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.yahoo.sql4d.indexeragent.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* A Queue that disallows duplicates. Thread safe.
*
* @author srikalyan
* @param <T>
*/
public class UniqueOnlyQueue<T> extends LinkedHashSet<T> {
private final Lock modifyLock = new ReentrantLock();
@Override
public boolean add(T e) {
try {
modifyLock.lock();
return super.add(e);
} finally {
modifyLock.unlock();
}
}
@Override
public boolean addAll(Collection<? extends T> c) {
try {
modifyLock.lock();
return super.addAll(c);
} finally {
modifyLock.unlock();
}
}
@Override
public boolean remove(Object o) {
try {
modifyLock.lock();
return super.remove(o);
} finally {
modifyLock.unlock();
}
}
@Override
public boolean removeAll(Collection<?> c) {
try {
modifyLock.lock();
return super.removeAll(c);
} finally {
modifyLock.unlock();
}
}
/**
* Special method that removes head element.
* @return
*/
public T removeFirst() {
try {
modifyLock.lock();
Iterator<T> it = iterator();
if (it.hasNext()) {
T item = it.next();
it.remove();
return item;
}
} finally {
modifyLock.unlock();
}
return null;
}
/**
* This method first removes all existing entries and zips together all the lists. For ex:
* [[x1, x2, x3] [y1, y2] [z1] [d1, d2, d3]] -> [x1, y1, z1, d1, x2, y2, d2, x3, d3]
* Assuming each list is per datasource and are sorted by id(latest on top) then
* by zipping we are picking one latest from each data source.
* @param kLists
*/
public void clearAndMergeKLists(List<List<T>> kLists) {
try {
// Beauty of reentrant locks. The same thread is going to acquire the
// same lock again multiple times during add.
modifyLock.lock();
clear();
List<Iterator<T>> kListIters = new ArrayList<>();
for (List<T> kThList:kLists) {
kListIters.add(kThList.iterator());
}
boolean hasNone = false;
while (!hasNone) {
hasNone = true;
List<Iterator<T>> markedForRemoval = new ArrayList<>();
for (Iterator<T> kThListIter:kListIters) {
if (kThListIter.hasNext()) {
hasNone = false;
add(kThListIter.next());
} else {
markedForRemoval.add(kThListIter);
}
}
kListIters.removeAll(markedForRemoval);
}
} finally {
modifyLock.unlock();
}
}
}