我正在寻找实现Collection接口的东西,我可以在其中添加同一对象的多个实例(基于给定的Comparator),但是该集合不能包含两次相同的对象标识(基于==运算符)。必须对集合进行排序,并且我必须能够删除一个特定元素(基于==运算符)。
换句话说,它必须满足以下测试用例:
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());
}
}
任何帮助?
谢谢!
答案 0 :(得分:1)
编辑:其实我认为这可以用new TreeSet<>(Ordering.natural().thenComparing(Ordering.arbitrary()))
(与番石榴的Ordering)
我之前使用MultiSet的建议不起作用。这是一个使用标准库的实现,使用Patricia的一些想法:
public class IdentityTreeSet<T extends Comparable> extends AbstractCollection<T>
{
private static final Object PRESENT = new Object();
private SortedMap<T, IdentityHashMap<T, Object>> values = new TreeMap<T, IdentityHashMap<T, Object>>();
@Override
public Iterator<T> iterator() {
return new Iterator<T>()
{
Iterator<IdentityHashMap<T, Object>> outerIterator = values.values().iterator();
IdentityHashMap<T, Object> currentMap = new IdentityHashMap();
Iterator<T> innerIterator = currentMap.keySet().iterator();
@Override
public boolean hasNext()
{
return innerIterator.hasNext() || outerIterator.hasNext();
}
@Override
public T next()
{
if (innerIterator.hasNext())
{
return innerIterator.next();
}
else
{
currentMap = outerIterator.next();
innerIterator = currentMap.keySet().iterator();
return next();
}
}
@Override
public void remove()
{
innerIterator.remove();
if (currentMap.isEmpty())
{
outerIterator.remove();
}
}
};
}
@Override
public int size()
{
int i = 0;
for (T t: this)
{
i++;
}
return i;
}
@Override
public boolean add(T e)
{
IdentityHashMap<T, Object> entrySet = values.get(e);
if (entrySet == null)
{
IdentityHashMap<T, Object> newList = new IdentityHashMap<T, Object>();
newList.put(e, PRESENT);
values.put(e, newList);
return true;
}
else
{
if (entrySet.containsKey(e))
{
return false;
}
entrySet.put(e, PRESENT);
return true;
}
}
@Override
public boolean remove(Object o)
{
IdentityHashMap<T, Object> entrySet = values.get(o);
if (entrySet == null)
{
return false;
}
else
{
if (! entrySet.containsKey(o))
{
return false;
}
else
{
entrySet.remove(o);
if (entrySet.isEmpty())
{
values.remove(o);
}
return true;
}
}
}
}
如果IdentityHashMap<T, Object>
被包装到新的类IdentityHashSet<T>
中,这会更干净,但是这个代码已经有点像StackOverflow的答案了,所以我将把它留作练习为了读者。
请注意,Collection.remove的文档是以equals
编写的,因此此类不能严格遵守Collection合同,如果作为Collection传递给代码,则可能会导致错误控制。
答案 1 :(得分:1)
如果现有的集合没有完全符合您的要求,请考虑以下策略:
定义一个类,其公共方法完全符合您的需要,不多也不少。
使用现有的集合来实现类来处理繁忙的工作,但是控制代码可以强制执行您的需求。
例如,您的类可能有一个TreeSet,其每个元素都是底层类的非空IdentityHashMap。 TreeSet比较器将从每个IdentityHashMap中提取一个元素并返回比较它们的结果。
要删除元素,首先检查删除它是否会清空其IdentityHashMap(它存在,并且设置大小为1)。如果是这样,请从TreeSet中删除IdentityHashMap。如果没有,请从IdentityHashMap中删除该元素。
这只是一个大纲,需要填写很多细节。重点是根据您编写的课程中已经存在的内容构建您想要的内容。
答案 2 :(得分:0)
关于你问题的这一部分“但是集合不能包含两倍相同的对象标识(基于==
运算符)”,如果你想要两个对象都等于等于==
运算符,您需要阅读有关实例控制的对象(基本上是散列自己的实例的对象,并返回相同的缓存对象,而不是在请求的对象已经存在时创建一个新对象。)