Java:根据大小

时间:2017-08-28 13:13:24

标签: java sorting set

我正在寻找一种对一组集合进行排序的方法 - 更具体地说,数据结构需要具有以下属性:

  • "外部"和#34;内部"集合应该是集合,即在包含的集合中不应该有重复的元素,也不应该有重复的集合。
  • 该集合应根据所包含集合的长度按升序排序,即{{A},{A,B},{A,C,F},{B,D,F}}有效而{{A,C,F},{A,B},{B,D,F},{A}}则不是。
  • 应该可以迭代各个包含的集合以及所有集合。当然,迭代所有集合应该根据它们各自的大小按升序列出它们。
  • 如果两组具有相同的大小,则顺序可以是任意的。

我的基本方法是TreeSet<TreeSet<T>>,但由于包含的TreeSet需要按自定义行为进行排序,因此我编写了一个包装类(省略了不必要的细节,例如其他构造函数和方法):

import java.util.TreeSet;
import java.util.Iterator;
import java.util.Collection;

public class WrappedSet< T > implements Comparable< WrappedSet< T >>, Iterable< T >  {
    protected TreeSet< T > set;

    public WrappedSet( Collection< T > collection ) {
        set = new TreeSet< T >( collection );
    }

    public int size() {
        return set.size();
    }

    @Override
    public Iterator< T > iterator() {
        return set.iterator();
    }

    @Override
    public int compareTo( WrappedSet< T > other ) {
        if ( set.equals( other.set )) {
            return 0;
        }
        else {
            return ( set.size() >= other.set.size()) ? 1 : -1;
        }
    }

    @Override
    public String toString() {
        return set.toString();
    }
}

为了显示问题,我正在使用这个主要功能:

public static void main( String[] args ) {
    TreeSet< WrappedSet< String >> wsets = new TreeSet< WrappedSet< String >>();

    for ( String arg : args ) {
        wsets.add( new WrappedSet< String >( new TreeSet< String >( Arrays.asList( arg.split( "," )))));
    }

    System.out.println( "Input sets: " + Arrays.toString( wsets.toArray()));
}

我理解如果这个类的.equals()基于测试各个集合的相等性,那么我的&#34;类[将]具有与equals&#34; <不一致的自然顺序/ em>,作为CompareTo documentation短语。我也明白这可能是输出(有时)不正确的原因。请考虑以下示例:

输入(命令行):A,B,E A,C,F B,D,F A A,B,E
输出:输入集:[[A],[A,B,E],[A,C,F],[B,D,F],[A,B,E]] < / p>

正如您所看到的,{A,B,E}出现两次,我想这是因为TreeSet遍历树以查找具有此值的现有条目,但最终因为顺序而找不到它元素的独立性与实际值无关 - 只有它们的大小。

我怎样才能以一致的方式实现理想的行为?请注意,这是作业。因此,不基于TreeSet的解决方案自然受到欢迎。

1 个答案:

答案 0 :(得分:0)

为了让你的外TreeSet接受两个与不同元素大小相同的不同集,你需要确定它们的排序顺序,即使你不关心它们的顺序。否则你的设置将无法按预期工作。

而不是内部TreeSet的包装类我认为我更喜欢只写一个比较器。但是要确保你的比较器在内部集合上定义了一个总排序,并且只声明两个集合等于它们确实相等,因为它们中只有一个应该在集合中。正如RealSkeptic和其他人在评论中所说,为了履行Comparator合同,您需要履行:

  

实施者必须确保所有sgn(compare(x, y)) == -sgn(compare(y, x))xy

(来自the documentation

这只是Comparator的一个非常粗略的例子,当大小相同时,它不仅仅是比较大小。设计一个仍然不能很好的例子并不难,但我会留给你为你的情况写出正确的比较器。

    Comparator<TreeSet<?>> compareBySize 
            = Comparator.comparing((TreeSet<?> s) -> s.size())
                    .thenComparing(TreeSet::toString);

    TreeSet<TreeSet<Character>> mySet = new TreeSet<>(compareBySize);
    mySet.addAll(Arrays.asList(
                new TreeSet<>(Arrays.asList('A', 'B', 'E')),
                new TreeSet<>(Arrays.asList('A', 'C', 'F')),
                new TreeSet<>(Arrays.asList('B', 'D', 'F')),
                new TreeSet<>(Arrays.asList('A')),
                new TreeSet<>(Arrays.asList('A', 'B', 'E'))
            ));
    System.out.println(mySet);

打印

[[A], [A, B, E], [A, C, F], [B, D, F]]

我认为这是正确的。

顺便说一句,通常建议对接口而不是实现进行编程。如果您想这样做,请根据您的进一步要求使用SortedSetNavigableSet界面。您需要进行两项更改:首先,比较器应该是(在我的示例中使用SortedSet):

    Comparator<SortedSet<?>> compareBySize 
            = Comparator.comparing((SortedSet<?> s) -> s.size())
                    .thenComparing(SortedSet::toString);

其次将您的设置声明为SortedSet<SortedSet<Character>> mySet,但仍然在实例化中使用new TreeSet<>(…)