C#:在SortedSet中通过多个属性比较两个对象

时间:2017-02-20 23:22:38

标签: java c#

我希望通过多个属性比较两个对象。

我们说我有一个名为学生的班级,每个学生都有名字和分数。我的愿望是创建一个SortedSet(在这里实现一个Comparer ),所以每当我将一个学生添加到该集合中时,他们将按照他们的分数进行排序,如果他们具有相同的分数,他们将按照他们的名字按字母顺序排列。

Java 8相当于:

TreeSet<Student> students = new TreeSet<>(
    Comparator.comparing(Student::getScore).thenComparing(Student::getName)
);

这可以使用Comparer<Student>.Create()还是以其他任何方式实现?

5 个答案:

答案 0 :(得分:3)

您可以将SortedSet与自定义比较器一起使用。实施有点棘手:

var s = new SortedSet<Student>(
    Comparer<Student>.Create((a, b) => {
        // This code implements comparison by score first
        var res= a.Score.CompareTo(b.Score);
        // Ties are resolved by name in alphabetic order
        return res != 0 ? res : a.Name.CompareTo(b.Name);
    })
);

答案 1 :(得分:2)

我放在一起的两个简单示例,您可以使用Student类型class Program { static void Main(string[] args) { var set = new SortedSet<Student>(new StudentComparer()); set.Add(new Student {Name = "Test", Score = 10}); set.Add(new Student { Name = "Tom", Score = 5 }); set.Add(new Student { Name = "Adam", Score = 90 }); set.Add(new Student { Name = "Adam", Score = 85 }); foreach (var setItem in set) { Debug.WriteLine($@"{setItem.Name} - {setItem.Score}"); } /* outputs: Tom - 5 Test - 10 Adam - 85 Adam - 90 */ } } class Student { public string Name { get; set; } public int Score { get; set; } } class StudentComparer : IComparer<Student> { public int Compare(Student x, Student y) { var result = x.Score.CompareTo(y.Score); if (result == 0) { result = x.Name.CompareTo(y.Name); } return result; } } 。然后,您将按照所需的顺序进行比较:

        var students = new List<Student>
        {
            new Student {Name = "Test", Score = 10},
            new Student {Name = "Tom", Score = 5},
            new Student {Name = "Adam", Score = 90},
            new Student {Name = "Adam", Score = 85}
        };


        var orderedList = students.OrderByDescending(s => s.Score)
            .ThenBy(s => s.Name);

        foreach (var student in orderedList)
        {
            Debug.WriteLine($@"{student.Name} - {student.Score}");
        }

        /*  outputs:
            Adam - 90
            Adam - 85
            Test - 10
            Tom - 5
        */

您也可以使用普通列表,并使用Linq:

processBackground()

答案 2 :(得分:0)

您可以使用SortedSet generic并提供IComparer<Student>根据您想要的顺序进行比较,即首先比较得分,如果它们相同则比较名称

class Program
{
    static void Main(string[] args)
    {
        var students = new List<Student>()
        {
            new Student()
            {
                Score = 10,
                Name = "David"
            },
            new Student()
            {
                Score = 4,
                Name = "Nik"
            },
            new Student()
            {
                Score = 10,
                Name = "Randy"
            }
        };


        SortedSet<Student> sortedStudents = new SortedSet<Student>(new StudentMultiCriteria());
        foreach (var student in students)
        {
            sortedStudents.Add(student);
        }

        foreach (var sortedStudent in sortedStudents)
        {
            Console.WriteLine(sortedStudent);
        }

    }

}

class Student
{
    public int Score { get; set; }
    public string Name { get; set; }

    public override string ToString()
    {
        return $"Score {Score}, Name {Name}";
    }

}

class StudentMultiCriteria : IComparer<Student>
{
    public int Compare(Student x, Student y)
    {
        // You do the comparison based on different fields here
        return x.Score.CompareTo(y.Score) == 0 ? x.Name.CompareTo(y.Name) : x.Score.CompareTo(y.Score);
    }
}

答案 3 :(得分:0)

我的不好,在我的评论中我应该说Icomparable<Student>。这将允许排序集合使用默认比较器。另外在我看来,当这样排序时,通常分数将按降序排序,但名称会升序。因此,您的课程可能如下所示:

class Student : IComparable<Student>
{
    public int score = 0;
    public string name = "";

    public int CompareTo(Student other)
    {
        if (score == other.score)
        {
            return name.CompareTo(other.name);
        }
        else
        {
            return other.score.CompareTo(score);
        }
    }
}

static void Main()
{
    SortedSet<Student> students = new SortedSet<Student>();
    students.Add(new Student { score = 20, name = "abcd" });
    students.Add(new Student { score = 10, name = "bcde" });
    students.Add(new Student { score = 10, name = "acde" });
    students.Add(new Student { score = 30, name = "cdef" });
    students.Add(new Student { score = 10, name = "abce" });
}

结果看起来像这样:

30, “CDEF”

20, “ABCD”

10, “ABCE”

10, “ACDE”

10, “BCDE”

答案 4 :(得分:0)

您可以编写一个通用比较器来模仿该API:

public class Comparator<T> : Comparer<T>
{
    readonly List<Func<T, T, int>> m_comparisons = new List<Func<T, T, int>>();

    Comparator()
    {
    }

    public static Comparator<T> Comparing<TProp>(Func<T, TProp> property)
        where TProp : IComparable<TProp>
    {
        return new Comparator<T>().ThenComparing(property);
    }

    public static Comparator<T> ComparingDescending<TProp>(Func<T, TProp> property)
        where TProp : IComparable<TProp>
    {
        return new Comparator<T>().ThenComparingDescending(property);
    }

    public static Comparator<T> Comparing<TProp>(Func<T, TProp> property, IComparer<TProp> propertyComparer)
    {
        return new Comparator<T>().ThenComparing(property, propertyComparer);
    }

    public static Comparator<T> ComparingDescending<TProp>(Func<T, TProp> property, IComparer<TProp> propertyComparer)
    {
        return new Comparator<T>().ThenComparingDescending(property, propertyComparer);
    }

    public Comparator<T> ThenComparing<TProp>(Func<T, TProp> property)
        where TProp : IComparable<TProp>
    {
        return ThenComparing(property, Comparer<TProp>.Default);
    }

    public Comparator<T> ThenComparingDescending<TProp>(Func<T, TProp> property)
        where TProp : IComparable<TProp>
    {
        return ThenComparingDescending(property, Comparer<TProp>.Default);
    }

    public Comparator<T> ThenComparing<TProp>(Func<T, TProp> property, IComparer<TProp> propertyComparer)
    {
        m_comparisons.Add((t1, t2) => propertyComparer.Compare(property(t1), property(t2)));
        return this;
    }

    public Comparator<T> ThenComparingDescending<TProp>(Func<T, TProp> property, IComparer<TProp> propertyComparer)
    {
        m_comparisons.Add((t1, t2) => propertyComparer.Compare(property(t2), property(t1)));
        return this;
    }

    public override int Compare(T x, T y)
    {
        foreach (Func<T, T, int> comparison in m_comparisons)
        {
            int result = comparison(x, y);
            if (result != 0)
            {
                return result;
            }
        }

        // They are equal.
        return 0;
    }
}

用法:

SortedSet<Student> students = new SortedSet<Student>(Comparator
    .Comparing(x => Score)
    .ThenComparing(x => x.Name)); // etc.

与使用Comparer<Student>.Create来构造一个大型lambda相比,我发现这更具可读性和安全性。