我需要创建基于以下内容的优先级集/数组:
public interface IListener
{
public Priority getPriority();
public enum Priority
{
HIGHEST,
HIGH,
NORMAL,
LOW,
LOWEST;
}
}
IListeners存储在:
HashMap<Class<? extends IListener>, Set<IListener>> listeners = new HashMap<>();
我希望制作的方法总是在优先级组之后的第一位添加IListener。 例: 给定Set包含一些具有此顺序的IListeners。
{最高,最高,高,高,低,低,低,最低}
添加优先级== HIGH的侦听器将导致:
{最高,最高,高,高,高,低,低,低,最低}
大胆加入一个。
我知道我可以在1st&#34;免费插槽&#34;上进行迭代和添加,但问题是 - Java是否提供了一些好看的(可能更好的?)解决方案?可能只是供将来参考。
答案 0 :(得分:2)
如评论所示,我不认为JDK中有任何完全符合您要求的集合。
IListenerSet
是符合您需求的Set
实施。迭代器始终按优先级顺序返回元素。如果两个元素具有相同的优先级,则按照它们放入集合的顺序返回它们。该集支持添加和删除。迭代器支持remove()
方法。该集不能包含null
,如果您尝试添加NullPointerException
,则会抛出null
。该集不能包含IListener
getPriority()
返回null
的{{1}},如果您尝试添加此类元素,则会抛出IllegalArgumentException
。
public final class IListenerSet<T extends IListener> extends AbstractSet<T> {
private final Map<IListener.Priority, Set<T>> map;
public IListenerSet() {
map = new EnumMap<>(IListener.Priority.class);
for (IListener.Priority p : IListener.Priority.values())
map.put(p, new LinkedHashSet<>());
}
public IListenerSet(Collection<? extends T> collection) {
this();
addAll(collection);
}
@Override
public int size() {
int size = 0;
for (Set<T> set : map.values())
size += set.size();
return size;
}
@Override
public boolean contains(Object o) {
if (!(o instanceof IListener))
return false;
IListener listener = (IListener) o;
IListener.Priority p = listener.getPriority();
return p != null && map.get(p).contains(listener);
}
@Override
public boolean add(T listener) {
IListener.Priority p = listener.getPriority();
if (p == null)
throw new IllegalArgumentException();
return map.get(p).add(listener);
}
@Override
public boolean remove(Object o) {
if (!(o instanceof IListener))
return false;
IListener listener = (IListener) o;
IListener.Priority p = listener.getPriority();
return p != null && map.get(p).remove(listener);
}
@Override
public void clear() {
for (Set<T> set : map.values())
set.clear();
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private Iterator<T> iterator = map.get(IListener.Priority.values()[0]).iterator();
private int nextIndex = 1;
private Iterator<T> nextIterator = null;
@Override
public boolean hasNext() {
if (iterator.hasNext() || nextIterator != null)
return true;
IListener.Priority[] priorities = IListener.Priority.values();
while (nextIndex < priorities.length) {
Set<T> set = map.get(priorities[nextIndex++]);
if (!set.isEmpty()) {
nextIterator = set.iterator();
return true;
}
}
return false;
}
@Override
public T next() {
if (iterator.hasNext())
return iterator.next();
if (!hasNext())
throw new NoSuchElementException();
iterator = nextIterator;
nextIterator = null;
return iterator.next();
}
@Override
public void remove() {
iterator.remove();
}
};
}
}
答案 1 :(得分:2)
另一种方法是将TreeSet
与自定义比较器一起使用,并自动将自动生成的id分配给添加到集合中的元素,因此后面的元素总是会得到更大的id,可用于比较:
public class IListenerSet extends AbstractSet<IListener> {
private long maxId = 0;
private final Map<IListener, Long> ids = new HashMap<>();
private final Set<IListener> set = new TreeSet<>(new Comparator<IListener>() {
@Override
public int compare(IListener o1, IListener o2) {
int res = o1.getPriority().compareTo(o2.getPriority());
if(res == 0)
res = ids.get(o1).compareTo(ids.get(o2));
return res;
}
});
@Override
public Iterator<IListener> iterator() {
return new Iterator<IListener>() {
Iterator<IListener> it = set.iterator();
private IListener e;
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public IListener next() {
this.e = it.next();
return e;
}
@Override
public void remove() {
it.remove();
ids.remove(e);
}
};
}
@Override
public int size() {
return set.size();
}
@Override
public boolean contains(Object o) {
return ids.containsKey(o);
}
@Override
public boolean add(IListener e) {
if(ids.get(e) != null)
return false;
// assign new id and store it in the internal map
ids.put(e, ++maxId);
return set.add(e);
}
@Override
public boolean remove(Object o) {
if(!ids.containsKey(o)) return false;
set.remove(o);
return true;
}
@Override
public void clear() {
ids.clear();
set.clear();
}
}
答案 2 :(得分:1)
保持简单:
您可以组合多种集合:
LinkedHashSet
允许您通过按顺序排序(并且没有重复的项目)来存储商品。TreeMap
允许您根据键映射键和值。因此,您可以声明此组合:
TreeMap<IListener.Priority, LinkedHashSet<IListener>> listenersByPriority=new TreeMap<IListener.Priority, LinkedHashSet<IListener>>(new PriorityComparator());
...并将其封装在适当的抽象中以进行管理:
public class ListenerManager
{
private final TreeMap<IListener.Priority, LinkedHashSet<IListener>> listenersByPriority=new TreeMap<IListener.Priority, LinkedHashSet<IListener>>();
private int size;
public void addListener(IListener listener)
{
synchronized (listenersByPriority)
{
LinkedHashSet<IListener> list=listenersByPriority.get(listener.getPriority());
if (list == null)
{
list=new LinkedHashSet<IListener>();
listenersByPriority.put(listener.getPriority(), list);
}
list.add(listener);
size++;
}
}
public Iterator<IListener> iterator()
{
synchronized (listenersByPriority)
{
List<IListener> output=new ArrayList<IListener>(size);
for (LinkedHashSet<IListener> list : listenersByPriority.values())
{
for (IListener listener : list)
{
output.add(listener);
}
}
return output.iterator();
}
}
}
当声明TreeMap
时,通常需要一个与密钥类相关的Comparator
的特定实现,但在这种情况下没有必要,因为枚举已经按其序数进行了比较。 (感谢Paul Boddington)。每个枚举项的序数是它在声明中的位置。