0

我正在寻找实现 Collection 接口的东西,我可以在其中添加同一对象的多个实例(基于给定的比较器),但集合不能包含两次相同的对象标识(基于 == 运算符)。必须对集合进行排序,并且我必须能够删除一个特定元素(基于 == 运算符)。

换句话说,它必须满足以下测试用例:

public MyCollection<E> implements Collection<E>
{ ... }

public class MyCollectionTest extends TestCase
{
   static class MyComparator implements Comparator<MyInterface>
   {
      @Override
      public int compare(MyInterface pO1, MyInterface pO2)
      {
         // the return type of getCategory() is just an enum.
         return pO1.getCategory().ordinal() - pO2.getCategory().ordinal();
      }
   }

   public void testAdd()
   {
      MyCollection<MyInterface> sut = new MyCollection<MyInterface>(new MyComparator());
      MyInterface svc1 = EasyMock.createNiceMock(MyInterface.class);
      MyInterface svc2 = EasyMock.createNiceMock(MyInterface.class);
      EasyMock.expect(svc1.getCategory()).andStubReturn(Category.CAR);
      EasyMock.expect(svc2.getCategory()).andStubReturn(Category.VAN);
      EasyMock.replay(svc1, svc2);
      sut.add(svc1);
      sut.add(svc2);
      assertEquals(2, sut.size());
      assertSame(svc1, sut.last());
      assertSame(svc2, sut.first());
   }

   public void testAddDouble()
   {
      MyCollection<MyInterface> sut = new MyCollection<MyInterface>(new MyComparator());
      MyInterface svc1 = EasyMock.createNiceMock(MyInterface.class);
      EasyMock.expect(svc1.getCategory()).andStubReturn(Category.CAR);
      sut.add(svc1);
      sut.add(svc1);
      assertEquals(1, sut.size());
   }

   public void testRemove()
   {
      MyCollection<MyInterface> sut = new MyCollection<MyInterface>(new MyComparator());
      MyInterface svc1 = EasyMock.createNiceMock(MyInterface.class);
      MyInterface svc2 = EasyMock.createNiceMock(MyInterface.class);
      EasyMock.expect(svc1.getCategory()).andStubReturn(Category.VAN);
      EasyMock.expect(svc2.getCategory()).andStubReturn(Category.VAN);
      EasyMock.replay(svc1, svc2);
      sut.add(svc1);
      sut.add(svc2);
      assertEquals(2, sut.size());
      sut.remove(svc1);
      assertEquals(1, sut.size());
   }
}

有什么帮助吗?

谢谢!

4

3 回答 3

1

编辑:实际上我认为这可以通过new TreeSet<>(Ordering.natural().thenComparing(Ordering.arbitrary()))(使用 Guava's Ordering)来解决


如果您没有 Guava,您可以使用 TreeMap 和 IdentityHashMap 自行开发,例如:

public class IdentityTreeSet<T extends Comparable> extends AbstractCollection<T> {

    private SortedMap<T, Set<T>> values = new TreeMap<>();

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            Iterator<Set<T>> outerIterator = values.values().iterator();
            Set<T> currentSet = Collections.newSetFromMap(new IdentityHashMap<>());
            Iterator<T> innerIterator = currentSet.iterator();

            @Override
            public boolean hasNext() {
                return innerIterator.hasNext() || outerIterator.hasNext();
            }

            @Override
            public T next() {
                if (innerIterator.hasNext()) {
                    return innerIterator.next();
                } else {
                    currentSet = outerIterator.next();
                    innerIterator = currentSet.iterator();
                    return next();
                }
            }

            @Override
            public void remove() {
                innerIterator.remove();
                if (currentSet.isEmpty()) {
                    outerIterator.remove();
                }
            }

        };
    }

    @Override
    public int size() {
        int i = 0;
        for (Set<T> set : values.values()) {
            i += set.size();
        }
        return i;
    }

    @Override
    public boolean add(T e) {
        Set<T> entrySet = values.get(e);

        if (entrySet == null) {
            Set<T> newSet = Collections.newSetFromMap(new IdentityHashMap<>());
            newSet.add(e);
            values.put(e, newSet);
            return true;
        } else {
            return entrySet.add(e);
        }
    }

    @Override
    public boolean remove(Object o) {
        Set<T> entrySet = values.get(o);

        if (entrySet == null) {
            return false;
        } else {
            boolean removed = entrySet.remove(o);
            if (entrySet.isEmpty()) {
                values.remove(o);
            }
            return removed;
        }
    }

}

请注意,Collection.remove的文档是用 来编写的equals,因此此类不能严格遵守 Collection 契约,并且如果作为 Collection 传递给您无法控制的代码,则可能会导致错误。

于 2013-08-30T14:17:44.777 回答
1

如果没有现有的集合完全符合您的要求,请考虑以下策略:

定义一个类,其公共方法完全符合您的需要,不多也不少。

使用现有集合实现该类以处理繁忙的工作,但使用您的代码来控制您的要求。

例如,您的类可能有一个 TreeSet,其每个元素都是底层类的非空 IdentityHashMap。TreeSet 比较器将从每个 IdentityHashMap 中提取一个元素并返回比较它们的结果。

要删除一个元素,首先检查删除它是否会清空其 IdentityHashMap(它存在,并且设置的大小为 1)。如果是这样,请从 TreeSet 中删除 IdentityHashMap。如果不是,则从其 IdentityHashMap 中删除该元素。

这只是一个大纲,有很多细节需要填写。重点是根据您编写的类中已经存在的内容来构建您想要的内容。

于 2013-08-30T14:35:12.573 回答
0

关于您问题的这一部分“但集合不能包含两次相同的对象标识(基于==运算符)”,如果您希望两个对象通过等号和==运算符都相等,您需要阅读实例控制对象(基本上是对象散列他们自己的实例,并返回相同的缓存对象,而不是在被请求的对象已经存在时创建一个新对象)。

于 2013-08-30T13:55:47.057 回答