Полное руководство. С# 4.0
Шрифт:
Содержимое массива нередко приходится сортировать. Для этой цели в классе Array предусмотрен обширный ряд сортирующих методов. Так, с помощью раз ных вариантов метода Sort можно отсортировать массив полностью или в задан ных пределах либо отсортировать два массива, содержащих соответствующие пары "ключ-значение". После сортировки в массиве можно осуществить эффективный по иск, используя разные варианты метода BinarySearch. В качестве примера ниже приведена программа, в которой демонстрируется применение методов Sort и BinarySearch для сортировки и поиска в массиве значений типа int. // Отсортировать массив и найти в нем значение. using System; class SortDemo { static void Main { int[] nums = { 5, 4, 6, 3, 14, 9, 8, 17, 1, 24, -1, 0 }; // Отобразить исходный порядок следования. Console.Write("Исходный порядок следования: "); foreach(int i in nums) Console.Write(i + " "); Console.WriteLine; // Отсортировать массив. Array.Sort(nums); // Отобразить порядок
Вот к какому результату приводит выполнение этой программы. Исходный порядок следования: 5 4 6 3 14 9 8 17 1 24 -1 0 Порядок следования после сортировки: -1 0 1 3 4 5 6 8 9 14 17 24 Индекс элемента массива со значением 14: 9
В приведенном выше примере массив состоит из элементов типа int, который от носится к категории типов значений. Все методы, определенные в классе Array, авто матически доступны для обработки массивов всех встроенных в C# типов значений. Но в отношении массивов ссылок на объекты это правило может и не соблюдаться. Так, для сортировки массива ссылок на объекты в классе типа этих объектов должен быть реализован интерфейс IComparable или IComparable. Если же ни один из этих интерфейсов не реализован в данном классе, то во время выполнения программы может возникнуть исключительная ситуация в связи с попыткой отсортировать по добный массив или осуществить в нем поиск. Правда, реализовать оба интерфейса, IComparable и IComparable<T>, совсем нетрудно.
В интерфейсе IComparable определяется один метод. int CompareTo(object obj)
В этом методе значение вызывающего объекта сравнивается со значением объекта, определяемого параметром obj. Если значение вызывающего объекта больше, чем у объекта obj, то возвращается положительное значение; если оба значения равны — нулевое значение, а если значение вызывающего объекта меньше, чем у объекта obj, — отрицательное значение.
Интерфейс IComparable является обобщенным вариантом интерфейса IComparable. Поэтому в нем определен следующий обобщенный вариант метода CompareTo. int CompareTo(Т other)
Обобщенный вариант метода CompareTo действует аналогично необобщенному его варианту. В нем значение вызывающего объекта также сравнивается со значением объекта, определяемого параметром other. Если значение вызывающего объекта боль ше, чем у объекта other, то возвращается положительное значение; если оба значения равны — нулевое значение, а если значение вызывающего объекта меньше, чем у объек та other, — отрицательное значение. Преимущество интерфейса IComparable за ключается в том, что он обеспечивает типовую безопасность, поскольку в этом случае тип обрабатываемых данных указывается явным образом, а следовательно, никакого приведения типа object сравниваемого объекта к нужному типу не требуется. В ка честве примера ниже приведена программа, в которой демонстрируются сортировка и поиск в массиве объектов определяемого пользователем класса. // Отсортировать массив объектов и осуществить в нем поиск. using System; class MyClass : IComparable<MyClass> { public int i; public MyClass(int x) { i = x; } // Реализовать интерфейс IComparable<MyClass>. public int CompareTo(MyClass v) { return i - v.i; } public bool Equals(MyClass v) { return i == v.i; } } class SortDemo { static void Main { MyClass[] nums = new MyClass[5]; nums[0] = new MyClass(5); nums[1] = new MyClass(2); nums[2] = new MyClass (3); nums[3] = new MyClass (4); nums[4] = new MyClass (1); // Отобразить исходный порядок следования. Console.Write("Исходный порядок следования: "); foreach(MyClass о in nums) Console.Write(о.i + " "); Console.WriteLine; // Отсортировать массив. Array.Sort(nums); // Отобразить порядок следования после сортировки. Console.Write("Порядок следования после сортировки: "); foreach(MyClass о in nums) Console.Write(о.i + " "); Console.WriteLine; // Найти объект MyClass (2). MyClass x = new MyClass(2); int idx = Array.BinarySearch(nums, x); Console.WriteLine("Индекс элемента массива с объектом MyClass(2): " + idx); } }
При выполнении этой программы получается следующий результат. Исходный порядок следования: 5 2 3 4 1 Порядок следования после сортировки: 1 2 3 4 5 Индекс элемента массива с объектом MyClass(2): 1
При сортировке или поиске в массиве строк может возникнуть потребность явно указать способ сравнения символьных строк. Так, если массив будет сортироваться с использованием одних настроек культурной среды, а поиск в нем — с помощью дру гих настроек, то во избежание ошибок, скорее всего, придется явно указать способ сравнения. Аналогичная ситуация возникает и в том случае, если требуется отсорти ровать массив символьных строк при настройках культурной среды, отличающихся от текущих. Для выхода из подобных ситуаций можно
ПРИМЕЧАНИЕ Более подробно особенности сравнения строк рассматриваются в главе 22.
Класс StringComparer объявляется в пространстве имен System и реализует, среди прочего, интерфейсы IComparer и IComparer<T>. Поэтому экземпляр объек та типа StringComparer может быть передан в качестве аргумента параметру типа IComparer. Кроме того, в классе StringComparer определен ряд доступных только для чтения свойств, возвращающих экземпляр объекта типа StringComparer и под держивающих различные способы сравнения символьных строк. Все эти свойства пе речислены ниже. Свойство Способ сравнения public static StringComparer CurrentCulture {get; } С учетом регистра и культурной среды public static StringComparer CurrentCultureIgnoreCase {get; } Без учета регистра, но с учетом культурной среды public static StringComparer InvariantCulture {get; } С учетом регистра и безотносительно к культурной среде public static StringComparer InvariantCultureIgnoreCase {get; } Без учета регистра и безотносительно к культурной среде public static StringComparer Ordinal {get; } Порядковое сравнение с учетом регистра public static StringComparer OrdinalIgnoreCase {get; } Порядковое сравнение без учета регистра
Передавая явным образом экземпляр объекта типа StringComparer, можно со вершенно однозначно определить порядок сортировки или поиска в массиве. Напри мер, в приведенном фрагменте кода сортировка и поиск в массиве символьных строк осуществляется с помощью свойства StringComparer.Ordinal. string[] strs = { "xyz", "one" , "beta", "Alpha" }; // ... Array.Sort(strs, StringComparer.Ordinal); int idx = Array.BinarySearch(strs, "beta", StringComparer.Ordinal); Обращение содержимого массива
Иногда оказывается полезно обратить содержимое массива и, в частности, отсорти ровать по убывающей массив, отсортированный по нарастающей. Для такого обраще ния массива достаточно вызвать метод Reverse. С его помощью можно обратить содержимое массива полностью иди частично. Этот процесс демонстрируется в при веденной ниже программе. // Обратить содержимое массива. using System; class ReverseDemo { static void Main { int[] nums = { 1, 2, 3, 4, 5 }; // Отобразить исходный порядок следования. Console.Write("Исходный порядок следования: "); foreach(int i in nums) Console.Write(i + " "); Console.WriteLine; // Обратить весь массив. Array.Reverse(nums); // Отобразить обратный порядок следования. Console.Write("Обратный порядок следования: "); foreach(int i in nums) Console.Write(i + " "); Console.WriteLine; // Обратить часть массива. Array.Reverse(nums, 1, 3); // Отобразить обратный порядок следования. Console.Write("Частично обращенный порядок следования: "); foreach(int i in nums) Console.Write(i + " "); Console.WriteLine; } }
Эта программа дает следующий результат. Исходный порядок следования: 1 2 3 4 5 Обратный порядок следования: 5 4 3 2 1 Частично обращенный порядок следования: 5 2 3 4 1
Копирование массива
Полное или частичное копирование одного массива в другой — это еще одна весь ма распространенная операция с массивами. Для копирования содержимого массива служит метод Сору. В зависимости от его варианта копирование элементов исхо дного массива осуществляется в начало или в средину целевого массива. Применение метода Сору демонстрируется в приведенном ниже примере программы. // Скопировать массив. using System; class CopyDemo { static void Main { int[] source = { 1, 2, 3, 4, 5 }; int[] target = { 11, 12, 13, 14, 15 }; int[] source2 = { -1, -2, -3, -4, -5 }; // Отобразить исходный массив. Console.Write("Исходный массив: "); foreach(int i in source) Console.Write(i + " "); Console.WriteLine; // Отобразить исходное содержимое целевого массива. Console.Write("Исходное содержимое целевого массива: "); foreach(int i in target) Console.Write(i,+ " "); Console.WriteLine; // Скопировать весь массив. Array.Copy(source, target, source.Length); // Отобразить копию. Console.Write("Целевой массив после копирования: "); foreach(int i in target) Console.Write(i + " "); Console.WriteLine; // Скопировать в средину целевого массива. Array.Copy(source2, 2, target, 3, 2); // Отобразить копию. Console.Write("Целевой массив после частичного копирования: "); foreach(int i in target) Console.Write(i + " "); Console.WriteLine; } }
Выполнение этой программы дает следующий результат. Исходный массив: 1 2 3 4 5 Исходное содержимое целевого массива: 11 12 13 14 15 Целевой массив после копирования: 1 2 3 4 5 Целевой массив после частичного копирования: 1 2 3 -3 -4 Применение предиката
Предикат представляет собой делегат типа System.Predicate, возвращающий логическое значение true иди false в зависимости от некоторого условия. Он объяв ляется следующим образом. public delegate bool Predicate<T> (T obj)