/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.util;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
/**
* Order preserving Set based on System.identityHashCode() for an object, which
* also supports Object->index lookup.
*
* @author Jim Procter (2016) based on Evgeniy Dorofeev's response: via
* https://stackoverflow.com/questions/17276658/linkedidentityhashset
*
*/
public class LinkedIdentityHashSet extends AbstractSet
{
LinkedHashMap set = new LinkedHashMap<>();
static class IdentityWrapper
{
Object obj;
public int p;
IdentityWrapper(Object obj, int p)
{
this.obj = obj;
this.p = p;
}
@Override
public boolean equals(Object obj)
{
return this.obj == ((IdentityWrapper) obj).obj;
}
@Override
public int hashCode()
{
return System.identityHashCode(obj);
}
}
@Override
public boolean add(E e)
{
IdentityWrapper el = (new IdentityWrapper(e, set.size()));
// Map.putIfAbsent() from Java 8
// return set.putIfAbsent(el, el) == null;
return putIfAbsent(el, el) == null;
}
/**
* If the specified key is not already associated with a value (or is mapped
* to null) associates it with the given value and returns null, else returns
* the current value.
*
* Method added for Java 7 (can remove for Java 8)
*
* @param key
* @param value
* @return
* @see https
* ://docs.oracle.com/javase/8/docs/api/java/util/Map.html#putIfAbsent
* -K-V-
*/
private IdentityWrapper putIfAbsent(IdentityWrapper key,
IdentityWrapper value)
{
IdentityWrapper v = set.get(key);
if (v == null)
{
v = set.put(key, value);
}
return v;
}
@Override
public Iterator iterator()
{
return new Iterator()
{
final Iterator se = set.keySet().iterator();
@Override
public boolean hasNext()
{
return se.hasNext();
}
@SuppressWarnings("unchecked")
@Override
public E next()
{
return (E) se.next().obj;
}
@Override
public void remove()
{
// Java 8 default behaviour
throw new UnsupportedOperationException();
}
};
}
@Override
public int size()
{
return set.size();
}
/**
* Lookup the index for e in the set
*
* @param e
* @return position of e in the set when it was added.
*/
public int indexOf(E e)
{
return set.get(e).p;
}
}