Comparisons and Sorts Within Collections
Do comparisons & sorts using the System.Collections classes in .NET, which help in finding an element to remove or returning the value of a key-and-value pair.
docs.microsoft.com
Check for Equality
If the collection is generic, then items are compared for equality according to the following guidelines:
- If type T implements the IEquatable<T> generic interface,
then the equality comparer is the Equals method of that interface. - If type T does not implement IEquatable<T>, Object.Equals is used.
In addition, some constructor overloads for dictionary collections an IEqualityComparer<T> implementation,
which is used to compare keys for equality.
We recommend that you derive from the EqualityComparer<T> class instead of implementing the IEqualityComparer<T> interface, because the EqualityComparer<T> class tests for equality using the IEquatable<T>.Equals method instead of the Object.Equals method.
Determine Sort Order
For comparing objects, there is the concept of a default comparer and an explicit comparer.
The default comparer relies on at least one of the objects being compared to implement the IComparable interface.
For a generic collection, equality comparison is determined according to the following:
- If type T implements the System.IComparable<T> generic interface,
then the default comparer is the IComparable<T>.CompareTo(T) method of that interface. - If type T implements the non-generic System.IComparable interface,
then the default comparer is the IComparable.CompareTo(Object) method that interface. - If type T doen't implement either interface,
then there is no default comparer, and a comparer or comparison delegate must be provided explicity.
Example:
using System; using System.Collections.Generic; // Simple business object. A PartId is used to identify the // type of part but the part name can change. public class Part : IEquatable<Part>, IComparable<Part> { public string PartName { get; set; } public int PartId { get; set; } public override string ToString() => $"ID: {PartId} Name: {PartName}"; public override bool Equals(object obj) => (obj is Part part) ? Equals(part) : false; public int SortByNameAscending(string name1, string name2) => name1?.CompareTo(name2) ?? 1; // Default comparer for Part type. // A null value means that this object is greater. public int CompareTo(Part comparePart) => comparePart == null ? 1 : PartId.CompareTo(comparePart.PartId); public override int GetHashCode() => PartId; public bool Equals(Part other) => other is null ? false : PartId.Equals(other.PartId); // Should also override == and != operators. } public class Example { public static void Main() { // Create a list of parts. var parts = new List<Part> { // Add parts to the list. new Part { PartName = "regular seat", PartId = 1434 }, new Part { PartName = "crank arm", PartId = 1234 }, new Part { PartName = "shift lever", PartId = 1634 }, // Name intentionally left null. new Part { PartId = 1334 }, new Part { PartName = "banana seat", PartId = 1444 }, new Part { PartName = "cassette", PartId = 1534 } }; // Write out the parts in the list. This will call the overridden // ToString method in the Part class. Console.WriteLine("\nBefore sort:"); parts.ForEach(Console.WriteLine); // Call Sort on the list. This will use the // default comparer, which is the Compare method // implemented on Part. parts.Sort(); Console.WriteLine("\nAfter sort by part number:"); parts.ForEach(Console.WriteLine); // This shows calling the Sort(Comparison<T> comparison) overload using // a lambda expression as the Comparison<T> delegate. // This method treats null as the lesser of two values. parts.Sort((Part x, Part y) => x.PartName == null && y.PartName == null ? 0 : x.PartName == null ? -1 : y.PartName == null ? 1 : x.PartName.CompareTo(y.PartName)); Console.WriteLine("\nAfter sort by name:"); parts.ForEach(Console.WriteLine); /* Before sort: ID: 1434 Name: regular seat ID: 1234 Name: crank arm ID: 1634 Name: shift lever ID: 1334 Name: ID: 1444 Name: banana seat ID: 1534 Name: cassette After sort by part number: ID: 1234 Name: crank arm ID: 1334 Name: ID: 1434 Name: regular seat ID: 1444 Name: banana seat ID: 1534 Name: cassette ID: 1634 Name: shift lever After sort by name: ID: 1334 Name: ID: 1444 Name: banana seat ID: 1534 Name: cassette ID: 1234 Name: crank arm ID: 1434 Name: regular seat ID: 1634 Name: shift lever */ } }
using System; using System.Collections.Generic; class Program { static Dictionary<Box, String> boxes; static void Main() { BoxSameDimensions boxDim = new BoxSameDimensions(); boxes = new Dictionary<Box, string>(boxDim); Console.WriteLine("Boxes equality by dimensions:"); Box redBox = new Box(8, 4, 8); Box greenBox = new Box(8, 6, 8); Box blueBox = new Box(8, 4, 8); Box yellowBox = new Box(8, 8, 8); AddBox(redBox, "red"); AddBox(greenBox, "green"); AddBox(blueBox, "blue"); AddBox(yellowBox, "yellow"); Console.WriteLine(); Console.WriteLine("Boxes equality by volume:"); BoxSameVolume boxVolume = new BoxSameVolume(); boxes = new Dictionary<Box, string>(boxVolume); Box pinkBox = new Box(8, 4, 8); Box orangeBox = new Box(8, 6, 8); Box purpleBox = new Box(4, 8, 8); Box brownBox = new Box(8, 8, 4); AddBox(pinkBox, "pink"); AddBox(orangeBox, "orange"); AddBox(purpleBox, "purple"); AddBox(brownBox, "brown"); } public static void AddBox(Box bx, string name) { try { boxes.Add(bx, name); Console.WriteLine("Added {0}, Count = {1}, HashCode = {2}", name, boxes.Count.ToString(), bx.GetHashCode()); } catch (ArgumentException) { Console.WriteLine("A box equal to {0} is already in the collection.", name); } } } public class Box { public Box(int h, int l, int w) { this.Height = h; this.Length = l; this.Width = w; } public int Height { get; set; } public int Length { get; set; } public int Width { get; set; } } class BoxSameDimensions : EqualityComparer<Box> { public override bool Equals(Box b1, Box b2) { if (b1 == null && b2 == null) return true; else if (b1 == null || b2 == null) return false; return (b1.Height == b2.Height && b1.Length == b2.Length && b1.Width == b2.Width); } public override int GetHashCode(Box bx) { int hCode = bx.Height ^ bx.Length ^ bx.Width; return hCode.GetHashCode(); } } class BoxSameVolume : EqualityComparer<Box> { public override bool Equals(Box b1, Box b2) { if (b1 == null && b2 == null) return true; else if (b1 == null || b2 == null) return false; return (b1.Height * b1.Width * b1.Length == b2.Height * b2.Width * b2.Length); } public override int GetHashCode(Box bx) { int hCode = bx.Height * bx.Length * bx.Width; return hCode.GetHashCode(); } } /* This example produces an output similar to the following: * Boxes equality by dimensions: Added red, Count = 1, HashCode = 46104728 Added green, Count = 2, HashCode = 12289376 A box equal to blue is already in the collection. Added yellow, Count = 3, HashCode = 43495525 Boxes equality by volume: Added pink, Count = 1, HashCode = 55915408 Added orange, Count = 2, HashCode = 33476626 A box equal to purple is already in the collection. A box equal to brown is already in the collection. * */
using System; using System.Collections; using System.Collections.Generic; class Program { static void Main(string[] args) { List<Box> Boxes = new List<Box>(); Boxes.Add(new Box(4, 20, 14)); Boxes.Add(new Box(12, 12, 12)); Boxes.Add(new Box(8, 20, 10)); Boxes.Add(new Box(6, 10, 2)); Boxes.Add(new Box(2, 8, 4)); Boxes.Add(new Box(2, 6, 8)); Boxes.Add(new Box(4, 12, 20)); Boxes.Add(new Box(18, 10, 4)); Boxes.Add(new Box(24, 4, 18)); Boxes.Add(new Box(10, 4, 16)); Boxes.Add(new Box(10, 2, 10)); Boxes.Add(new Box(6, 18, 2)); Boxes.Add(new Box(8, 12, 4)); Boxes.Add(new Box(12, 10, 8)); Boxes.Add(new Box(14, 6, 6)); Boxes.Add(new Box(16, 6, 16)); Boxes.Add(new Box(2, 8, 12)); Boxes.Add(new Box(4, 24, 8)); Boxes.Add(new Box(8, 6, 20)); Boxes.Add(new Box(18, 18, 12)); // Sort by an Comparer<T> implementation that sorts // first by the length. Boxes.Sort(new BoxLengthFirst()); Console.WriteLine("H - L - W"); Console.WriteLine("=========="); foreach (Box bx in Boxes) { Console.WriteLine("{0}\t{1}\t{2}", bx.Height.ToString(), bx.Length.ToString(), bx.Width.ToString()); } Console.WriteLine(); Console.WriteLine("H - L - W"); Console.WriteLine("=========="); // Get the default comparer that // sorts first by the height. Comparer<Box> defComp = Comparer<Box>.Default; // Calling Boxes.Sort() with no parameter // is the same as calling Boxs.Sort(defComp) // because they are both using the default comparer. Boxes.Sort(); foreach (Box bx in Boxes) { Console.WriteLine("{0}\t{1}\t{2}", bx.Height.ToString(), bx.Length.ToString(), bx.Width.ToString()); } // This explicit interface implementation // compares first by the length. // Returns -1 because the length of BoxA // is less than the length of BoxB. BoxLengthFirst LengthFirst = new BoxLengthFirst(); Comparer<Box> bc = (Comparer<Box>) LengthFirst; Box BoxA = new Box(2, 6, 8); Box BoxB = new Box(10, 12, 14); int x = LengthFirst.Compare(BoxA, BoxB); Console.WriteLine(); Console.WriteLine(x.ToString()); } } public class BoxLengthFirst : Comparer<Box> { // Compares by Length, Height, and Width. public override int Compare(Box x, Box y) { if (x.Length.CompareTo(y.Length) != 0) { return x.Length.CompareTo(y.Length); } else if (x.Height.CompareTo(y.Height) != 0) { return x.Height.CompareTo(y.Height); } else if (x.Width.CompareTo(y.Width) != 0) { return x.Width.CompareTo(y.Width); } else { return 0; } } } // This class is not demonstrated in the Main method // and is provided only to show how to implement // the interface. It is recommended to derive // from Comparer<T> instead of implementing IComparer<T>. public class BoxComp : IComparer<Box> { // Compares by Height, Length, and Width. public int Compare(Box x, Box y) { if (x.Height.CompareTo(y.Height) != 0) { return x.Height.CompareTo(y.Height); } else if (x.Length.CompareTo(y.Length) != 0) { return x.Length.CompareTo(y.Length); } else if (x.Width.CompareTo(y.Width) != 0) { return x.Width.CompareTo(y.Width); } else { return 0; } } } public class Box : IComparable<Box> { public Box(int h, int l, int w) { this.Height = h; this.Length = l; this.Width = w; } public int Height { get; private set; } public int Length { get; private set; } public int Width { get; private set; } public int CompareTo(Box other) { // Compares Height, Length, and Width. if (this.Height.CompareTo(other.Height) != 0) { return this.Height.CompareTo(other.Height); } else if (this.Length.CompareTo(other.Length) != 0) { return this.Length.CompareTo(other.Length); } else if (this.Width.CompareTo(other.Width) != 0) { return this.Width.CompareTo(other.Width); } else { return 0; } } }
'.NET > C#' 카테고리의 다른 글
Concurrency (동시성) (0) | 2023.08.16 |
---|---|
Marshaling: 복사 및 고정 (0) | 2021.10.15 |
Array Marshaling (0) | 2021.10.15 |
Debugging Tips (0) | 2021.09.15 |
Equals, IEquatable<T> (0) | 2021.08.15 |