在添加到Set时按枚举排序

时间:2015-10-04 23:45:43

标签: java sorting

我需要创建基于以下内容的优先级集/数组:

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是否提供了一些好看的(可能更好的?)解决方案?可能只是供将来参考。

3 个答案:

答案 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)。每个枚举项的序数是它在声明中的位置。