web-dev-qa-db-ja.com

TreeSetでは、さまざまなプロパティに基づくカスタムオブジェクトの並べ替えと一意性

以下は私の学生クラスです

class Student implements Comparable {
   String name;
   int rollNo;

   @Override
   public int compareTo(Object obj) {
        return ((Student)obj).name.compareTo(this.name);
   }
} 

最新の変更:しかし、それでも正しい結果が得られない

@Override
public int compareTo(Object obj) {
    Student s = (Student) obj;
    if (name.equals(s.name)) { // achieving uniqueness
        return 0;
    } else {
        if (rollNo < s.rollNo) {
            return -1;
        } else if (rollNo > s.rollNo) {
            return 1;
        } else {
            // this makes `name` the second ordering option.
            // names don't equal here
            return name.compareTo(s.name);
        }
    }
}

TreeSet <Student>のオブジェクトを作成すると、一意の名前に基づいて、名前順に並べられたStudentオブジェクトのリストが表示されます。

ただし、TreeSet <Student>にstudent-rollNoで並べ替えた一意のstudent-nameが必要です。

コンパレータで可能ですか?誰かが私を助けることができます、すべての提案はありがたいです。ありがとう。

更新:ここに完全なプログラムがあります:

public class Student implements Comparable {

    int rollNo;
    String name;

    Student(String n,int rno) {
        rollNo=rno;
        name=n;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        TreeSet<Student> ts = new TreeSet<Student>();
        ts.add(new Student("bbb",2));
        ts.add(new Student("aaa",4));
        ts.add(new Student("bbb",2));
        ts.add(new Student("ccc",3));
        ts.add(new Student("aaa",1));
        ts.add(new Student("bbb",2));
        ts.add(new Student("bbb",5));

        System.out.println(ts);

    }

    @Override
    public int compareTo(Object obj) {
        Student s = (Student) obj;
        if (name.equals(s.name)) { // achieving uniqueness
            return 0;
        } else {
            if (rollNo < s.rollNo) {
                return -1;
            } else if (rollNo > s.rollNo) {
                return 1;
            } else {
                // this makes `name` the second ordering option.
                // names don't equal here
                return name.compareTo(s.name);
            }
        }
    }

    @Override
    public String toString() {
        return name + rollNo;
    }
}

更新:2:ご提案ありがとうございます。まだまだ必要です:)



/*
 * Actual scenario is having different properties,
 * So here I am just relating my actual scenario with Student class
 */
class Student implements Comparable {
    // sorting required on rollNo
    int rollNo;
    // Unique name is required
    String name;

    Student(String n, int rno) {
        rollNo = rno;
        name = n;
    }

    /**
     * 
     * @param args
     */
    public static void main(String[] args) {

        TreeSet<Student> tsName = new TreeSet<Student>();
        // here by default, order & uniqueness by name only
        tsName.add(new Student("ccc", 2));
        tsName.add(new Student("aaa", 4));
        tsName.add(new Student("ddd", 1));
        tsName.add(new Student("bbb", 3));
        tsName.add(new Student("ddd", 5));
        // output: aaa:4, bbb:3, ccc:2, ddd:1
        System.out.println(tsName);

        // creating new comparator for student RollNo
        TreeSet<Student> tsRollNo = new TreeSet<Student>(new Comparator<Student>() {
                    public int compare(Student stud1, Student stud2) {
                        return new Integer(stud1.rollNo).compareTo(stud2.rollNo);
                    }
                });
        tsRollNo.addAll(tsName);
        System.out.println(tsRollNo);
        // now got the desire output: ddd:1, ccc:2, bbb:3, aaa:4
    }

    public boolean equals(Object obj) {
        // internally not used to check equality while adding objects
        // in TreeSet
        System.out.println("equals() for " + this + " & " + ((Student) obj));
        return false;// return false/true doesn't make any sense here
    }

    @Override
    public int compareTo(Object obj) {
        Student s = (Student) obj;
        // internally inside TreeSet, compareTo is used to decide
        // whether two objects are equal or not,
        // i.e. compareTo will return 0 for same object(here student name)
        System.out.println("compareTo() for " + this + " & " + ((Student) obj));
        // achieving uniqueness
        return name.compareTo(s.name);
    }

    @Override
    public String toString() {
        return name + ":" + rollNo;
    }
}

[〜#〜] output [〜#〜]

compareTo() for aaa:4 & ccc:2
compareTo() for ddd:1 & ccc:2
compareTo() for bbb:3 & ccc:2
compareTo() for bbb:3 & aaa:4
compareTo() for ddd:5 & ccc:2
compareTo() for ddd:5 & ddd:1
[aaa:4, bbb:3, ccc:2, ddd:1]
[ddd:1, ccc:2, bbb:3, aaa:4]

友達、2つのコンパレータを使って得たものは何でも、オブジェクトを追加しながら同じことを達成することは可能ですか?最初に要素を追加してから、新しいコンパレータを使用して目的の順序を実現することはできません。
私は何千もの値を操作しているので、パフォーマンスも考慮する必要があります。

10
SmartSolution

in TreeSetソートと一意のチェックのための要素を追加しながら、コンパレータを使用します。

ここで問題となるのは、ロール番号にコンパレータを使用すると、ロール番号と一意のロール番号でソートされることです。ツリーセットで両方を一緒にすることはできません。

私はあなたが行くことをお勧めします。

  1. TreeSetここでは、重複の削除に集中します
  2. 次に、一意のデータを取得したら、ArrayListに移動し、任意の順序で並べ替えます
5
Jigar Joshi

注文

@ ralphによる回答 指定されたコンパレータでTreeSetを使用することは良いことです、それを使用してください。

設計

生のコレクションを使用するだけでなく、正しい動作を公開して文書化するクラス内に「学生データベース」の概念をラップする必要があります。特定の順序で学生のリストを取得することが設計要件である場合は、メソッドを公開します(おそらく、それを示す_Iterable<Student>_を返します。舞台裏では、使用パターンに応じてさまざまなことを実行できます。

  • 1つ以上のSetsまたはMapsを維持し、関心のある分野で学生を並べ替え/索引付けします。
  • Arrays.sort()と指定されたComparatorを使用したオンデマンドのインプレース配列ソート。

例...

_final class StudentTable {
   private static final Comparator<Student> studentRollNoComparator = ...;
   private final SortedSet<Student> sortedByRollNo = 
      new TreeSet<Student>(studentRollNoComparator);

   public Iterable<Student> studentsOrderedByRollNo()
   {
      return sortedByRollNo;
   } 

   //see below
   public void addStudent(final Student foo) { ... }
}
_

独自性

学生名のみを比較するには、Studentクラスのequals()hashCode()をオーバーライドする必要があります。次に、TreeSetに(サイレントに)一意性を取得します。明らかに、これを行う場合は、newStudentを挿入する前にstudentSet.contains(newStudent)かどうかを確認するために防御的にコーディングする必要があるため、重複があるかどうかがわかります。

_final class Student implements Comparable {
   ...

   @Override
   public boolean equals(Object o)
   {
      return o!=null &&  
             o (instanceof Student) &&
             ((Student)o).name.equals(this.name);
   }

   @Override
   public int hashCode()
   {
      return name.hashCode();  // good enough for this purpose
   } 
}
_

これを設定すると、学生を挿入するコードは次のようになります。

_void addNewStudent(final Student toAdd)
{
   if (studentSet.contains(toAdd)) { 
      throw new IllegalStateException("Student with same name as "+toAdd+" already exists.");
   }

   studentSet.add(toAdd);
}
_

その場合、ツリーセットは名前が一意の学生でいっぱいになり、そうでない場合は追加操作で失敗が報告されます。 (例外をスローすることは、1つの潜在的なルートにすぎず、重複した名前の学生を追加することが実際には例外的な条件である場合にのみ適切ですが、あなたは言いませんでした。)

4
andersoj

別のコンパレータを使用して新しいTreeSetを初期化できます。 -つまり、新しいコンパレータを作成し(Java.util.Comparatorインターフェイスを実装)、このコンパレータを使用して新しいTreeSetを初期化し、すべての学生をセットに追加するだけです。

TreeSet<Student> sortedByRollNo new TreeSet<Student>(new RollNoComparator());
sortedByRollNo.addAll(allStudents);

TreeSet<Student> sortedByY new TreeSet<Student>(new YComparator());
sortedByY.addAll(allStudents);

各ツリーセットは、ソート用に独自のコンパレータを持つことができます。コンパレータが指定されていない場合、ツリーセットはセット要素の自然順序を使用します。

追加

Uniqe Studentという名前だけが必要な場合は、次の2つの方法があります。

  • 学生の名前が等しい場合に0を返すようにコンパレータを実装します(しかし、これは一種のハックだと思います)。
  • 最初に生徒を名前でフィルタリングし、次にrollNoで並べ替えます。

このようなビット:

TreeSet<Student> sortedByRollNo new TreeSet<Student>(new RollNoComparator());
sortedByRollNo.addAll(new TreeSet<Student>(allStudends)); //this uses the native comparator to filter by uniqe name
1
Ralph

ここで遅れて申し訳ありませんが、ここにエレガントな解決策があります:

    public class OwnSortedList<T> extends TreeSet<T> {

    private static final long serialVersionUID = 7109828721678745520L;


    public OwnSortedList(Comparator<T> levelScoreComparator) {
        super(levelScoreComparator);
    }


    public boolean add(T e) {
        boolean existsElement = false;

        Iterator<T> it = iterator();
        while(it.hasNext() && !existsElement){
            T nextElement = it.next();
            if(nextElement.equals(e)){
                // Element found
                existsElement = true;
                Comparator<? super T> comparator = comparator();
                int compare = comparator.compare(nextElement, e);
                if(compare > 0){
                    remove(nextElement);
                    super.add(e);
                    //element added so return true
                    return true;
                }

            }
        }

        if(!existsElement){
            super.add(e);
        }

        return false;
    }
}
0
RamonBoza