有限的SortedSet

时间:2011-12-05 08:12:44

标签: java api sortedset

我正在寻找具有有限数量元素的SortedSet的实现。因此,如果添加了更多元素,则指定的最大值比较器决定是否添加项目并从集合中删除最后一个。

SortedSet<Integer> t1 = new LimitedSet<Integer>(3);
t1.add(5);
t1.add(3);
t1.add(1);
// [1,3,5]
t1.add(2);
// [1,2,3]
t1.add(9);
// [1,2,3]
t1.add(0);
// [0,1,2]

标准API中是否有一种优雅的方式来实现这一目标?

我已经编写了一个JUnit Test来检查实现:

@Test
public void testLimitedSortedSet() {
final LimitedSortedSet<Integer> t1 = new LimitedSortedSet<Integer>(3);
t1.add(5);
t1.add(3);
t1.add(1);
System.out.println(t1);
// [1,3,5]
t1.add(2);
System.out.println(t1);
// [1,2,3]
t1.add(9);
System.out.println(t1);
// [1,2,3]
t1.add(0);
System.out.println(t1);
// [0,1,2]
Assert.assertTrue(3 == t1.size());
Assert.assertEquals(Integer.valueOf(0), t1.first());
}

3 个答案:

答案 0 :(得分:4)

使用标准API,您必须自己完成,即扩展其中一个已排序的集合类,并将所需的逻辑添加到add()addAll()方法中。不应该太难。

是的,我不完全理解你的例子:

t1.add(9);
// [1,2,3]

之后该套装不应包含[1,2,9]吗?

编辑:我想我现在明白了:你只想保留添加到集合中的最小3个元素,对吗?

编辑2 :示例实现(未优化)可能如下所示:

class LimitedSortedSet<E> extends TreeSet<E> {

  private int maxSize;

  LimitedSortedSet( int maxSize ) {
    this.maxSize = maxSize;
  }

  @Override
  public boolean addAll( Collection<? extends E> c ) {
    boolean added = super.addAll( c );        
    if( size() > maxSize ) {
      E firstToRemove = (E)toArray( )[maxSize];
      removeAll( tailSet( firstToRemove ) );
    }   
    return added;
  }

  @Override
  public boolean add( E o ) {    
    boolean added =  super.add( o );
    if( size() > maxSize ) {
      E firstToRemove = (E)toArray( )[maxSize];
      removeAll( tailSet( firstToRemove ) );
    }
    return added;
  }
}

<击> 请注意,tailSet()返回包含参数的子集(如果在集合中)。这意味着如果您无法计算下一个更高的值(不需要在集合中),您将必须读取该元素。这是在上面的代码中完成的。

<击> 如果您可以计算下一个值,例如如果你有一组整数,做一些tailSet( lastElement + 1 )就足够了,你不必读取最后一个元素。

<击> 或者,您可以自己迭代该集合,并删除您想要保留的最后一个元素。

<击> 另一个替代方案,虽然可能更多工作,但是在插入元素之前检查大小并相应地移除。

更新:正如msandiford正确指出的那样,应该删除的第一个元素是索引为maxSize的元素。因此,不需要读取(重新添加?)最后想要的元素。

重要提示: 正如@DieterDP正确指出的那样,上面的实现违反了Collection#add() api合同,该合同规定如果一个集合因为重复而拒绝添加元素,那么必须抛出。

在上面的示例中,元素首先被添加,但由于大小限制可能会被删除,或者可能会删除其他元素,因此违反了合同。

要解决此问题,您可能需要更改add()addAll()以在这些情况下抛出异常(或者在任何情况下为了使它们无法使用)并提供替代方法来添加元素不违反任何现有的api合同。

在任何情况下都应该小心使用上面的示例 ,因为将它与不知道违规的代码一起使用可能会导致不必要的和难以调试的错误。

答案 1 :(得分:2)

不,使用现有的Java库没有类似的东西。

但是,是的,您可以使用合成来构建一个类似下面的一个。我相信这很容易。

public class LimitedSet implements SortedSet {

    private TreeSet treeSet = new TreeSet();

    public boolean add(E e) {
        boolean result = treeSet.add(e);
        if(treeSet.size() >= expectedSize) {
            // remove the one you like ;)
        }
        return result;
    }

    // all other methods delegate to the "treeSet"

}

<强>更新 阅读完评论后

因为您需要始终删除最后一个元素:

  • 您可以考虑在内部维护堆栈
  • 用O(n)
  • 会增加内存复杂度
  • 但可以使用O(1)...常量时间
  • 检索最后一个元素

它应该做我相信的技巧

答案 2 :(得分:2)

我会说这是装饰器模式的典型应用程序,类似于Collections类公开的装饰器集合:unmodifiableXXX,synchronizedXXX,singletonXXX等。我会把Guava的ForwardingSortedSet作为基类,然后写用您所需的功能装饰现有SortedSet的类,如下所示:

public final class SortedSets {

    public <T> SortedSet<T> maximumSize(
        final SortedSet<T> original, final int maximumSize){

        return new ForwardingSortedSet<T>() {

            @Override
            protected SortedSet<T> delegate() {
                return original;
            }

            @Override
            public boolean add(final T e) {
                if(original.size()<maximumSize){
                    return original.add(e);
                }else return false;
            }

            // implement other methods accordingly
        };
    }

}