C#. Урок 8. Массивы

Одной из наиболее часто используемых структур данных является массив. О том как работать с массивами в C# вы узнаете в этом уроке.

Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.

Объявление массивов и инициализация массивов

Объявление массивов

Массив – это структура данных для хранения элементом определенного типа, имеющая фиксированный размер. Доступ к элементам массива производится по числовому индексу.

Для объявления массива, после указания типа его элементов, ставятся квадратные скобки:

int[] a1; // массив типа int

Перед использованием, массив обязательно нужно проинициализировать, это можно сделать сразу, при его объявлении:

int[] na2 = new int[5]; // массив из пяти элементов типа int

Либо после объявления:

int[] na3;
na3 = new int[5]; // массив из пяти элементов типа int

Для доступа к элементам массива используются числовые индексы. Значения элементов массива будут равны значению по умолчанию для типа, массив которого был создан.

Например, для указанного выше a3 – это будут нули, так как для типа int значение по умолчанию: 0;

Console.WriteLine(na3[0]); // значение: 0
Console.WriteLine(na3[1]); // значение: 0

Если попытаться вывести элементы массива na1:

Console.WriteLine(na1[0]); // ошибка компиляции

то приложение не будет собрано, т.к. массив предварительно нужно проинициализировать.

Инициализация массивов

Рассмотрим различные варианты инициализации массива. Как уже было сказано, можно просто указать количество элементов в массиве, при этом его элементам будут присвоены значения по умолчанию:

bool[] ba1 = new bool[3];
Console.WriteLine("ba1[0]: " + ba1[0].ToString());

После объявления массива значения элементам присваиваются через индекс:

string[] sa1 = new string[3];
sa1[0] = "abc";
sa1[1] = "def";
sa1[2] = "ghi";
Console.WriteLine($"sa1: {sa1[0]}, {sa1[1]}, {sa1[2]}");

Есть возможность задать конкретные значения в момент объявления с использованием ключевого слова new и указанием типа:

double[] da1 = new double[3] {0.1, 0.2, 0.3};
Console.WriteLine($"da1: {da1[0]}, {da1[1]}, {da1[2]}");

Либо без ключевого слова new:

double[] da2 = {0.4, 0.5, 0.6};
Console.WriteLine($"da2: {da2[0]}, {da2[1]}, {da2[2]}");

Неявная типизация

При объявлении массива можно воспользоваться ключевым словом var. При этом тип элементов массива следует задать явно в правой части объявления:

var va2 = new string[3];
va2[0] = "John";
va2[1] = "Mary";
va2[2] = "Mike";
Console.WriteLine($"va2: {va2[0]}, {va2[1]}, {va2[2]}");

Либо предоставить возможность “поработать” системе вывода типов:

var va1 = new[] {1, 2, 3};
Console.WriteLine($"va1: {va1[0]}, {va1[1]}, {va1[2]}");

Доступ к элементам массива. Обход элементов массива.

Как уже было сказано выше, за доступ к элементам массива отвечают числовые индексы: 

int[] na4 = {1, 2, 3, 4, 5};
Console.WriteLine($"na4[0]: {na4[0]}");

При этом, если вы укажете индекс больше, чем максимально возможный, то будет выброшено исключение:

Console.WriteLine($"na4[10]: {na4[10]}");

Приведенная выше строка приведет к выбросу следующего исключения:

Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.

Обход элементов массива можно производить с помощью циклов for, foreach и while, последний самый неудобный для работы с массивами, его мы рассматривать не будем. Если вы работаете с циклом for, то для указания верхней границы инкрементируемой переменной можно воспользоваться свойством Length у массива:

for(int i = 0; i < na4.Length; i++)
{
    Console.Write(na4[i].ToString() + " ");
} // 1 2 3 4 5

Более удобным для обхода элементов будет foreach:

foreach(var v in na4)
{
    Console.Write(v.ToString() + " ");
} // 1 2 3 4 5

Преимущество цикла for состоит в том, что в нем вы можете модифицировать элементы массива:

for(int i = 0; i < na4.Length; i++)
{
    na4[i] = (na4[i] + 3) * 10;
    Console.Write(na4[i].ToString() + " ");
} // 40 50 60 70 80

Передача массива в метод

Массивы являются ссылочным типом данных, это означает, что их значения хранятся в куче, а имя переменной массива является ссылкой на соответствующую область памяти. При передаче массива в качестве аргумента в метод, происходит присваивание значения переменной массива переменной определяющей аргумент, а так как имя массива – это ссылка, то фактически происходит передача ссылки на значение в куче. Поэтому, если вы передали массив в функцию и внутри этой функции произошла модификация этого массива, то исходный массив тоже изменится.

Создадим метода WorkWithArray, который изменяет содержимое массива:

public static void WorkWithArray(int[] arr)
{        
    arr[0] = 123;
}

Вызовем его в методе Main:

int[] na5 = {1, 2, 3, 4, 5};           
foreach(var v in na5) // 1 2 3 4 5
    Console.Write(v + " ");
         
Console.WriteLine();

WorkWithArray(na5);

foreach(var v in na5) // 123 2 3 4 5
    Console.Write(v + " ");

Ниже приведена иллюстрация того, как массив и ссылки на него располагаются в памяти.

Переменные na5 в методе Main и arr в методе WorkWithArray ссылаются на одну и ту же область памяти в куче, поэтому изменение массива через переменную arr отражается на переменной na5.

Многомерные массивы

Массивы имеющее более одного измерения называются многомерными. До этого мы работали с одномерными массивами. В C# предлагается к использованию два вида многомерных массивов: прямоугольные и зубчатые, которые иногда называются массивы массивов.

Прямоугольные массивы

Прямоугольные массивы могут содержать несколько измерений (два и более), при этом количество элементов в каждом подизмерении (в каждой строке) одинаково.

Рассмотрим на примерах работу с такими массивами:

double[,] dm1 = new double[3, 3];
for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++)
        dm1[i, j] = i + j;

for (int i = 0; i < 3; i++)
{
    for (int j = 0; j < 3; j++)
        Console.Write($"{dm1[i, j]} ");
    Console.WriteLine();
}
Console.WriteLine();

double[,] dm2 = { { 1, 2, 3 }, { 4, 5, 6 } };
for (int i = 0; i < 2; i++)
{
    for (int j = 0; j < 3; j++)
        Console.Write($"{dm2[i, j]} ");
    Console.WriteLine();
}
Console.WriteLine();

Зубчатые массивы

В зубчатых массивах элементами верхнего уровня являются другие массивы, это позволяет создавать многомерные структуры, у которых строки имеют разную длину:

int[][] nm1 = new int[3][];
for(int i = 0; i < nm1.Length; i++)           
    nm1[i] = new int[i+1];
 
for(int i = 0; i < nm1.Length; i++)
{
    for(int j = 0; j < nm1[i].Length; j++)
        Console.Write($"{nm1[i][j]} ");
 
    Console.WriteLine();
}

Класс System.Array

Класс System.Array является базовым для всех массивов, это позволяет использовать свойства и методы данного класса при работе с массивом. Ниже в таблицах приведены некоторые полезные свойства и методы из System.Array, более полную информацию вы можете найти в официальной документации Microsoft (https://docs.microsoft.com/ru-ru/dotnet/api/system.array).

Свойства класса System.Array

Имя свойства

Назначение

Length

Число элементов в массиве. Учитываются все измерения.

Rank

Ранг массива – число измерений.

int[] na6 = {1, 2, 3, 4, 5, 6, 7};
int[,] nm2 = { {1, 2, 3}, {4, 5, 6}};
int[][] nm3 = new int[3][];
for(int i = 0; i < nm3.Length; i++)           
    nm3[i] = new int[i+1];

Console.WriteLine($"na6: Length={na6.Length}, Rank={na6.Rank}"); // na6: Length=7, Rank=1
Console.WriteLine($"nm2: Length={nm2.Length}, Rank={nm2.Rank}"); // nm2: Length=6, Rank=2
Console.WriteLine($"nm3: Length={nm3.Length}, Rank={nm3.Rank}"); // nm3: Length=3, Rank=1

Методы класса System.Array

Символ * после названия метода означает, что он имеет более одной сигнатуры, за дополнительной информацией обращайтесь к официальной документации.

Имя метода

Назначение

BinarySearch(Array, Object)*

Выполняет поиск элемента в массиве.

Clear(Array, Int32, Int32)

Присваивает значение по умолчанию определенному количеству элементов массива начиная с заданного индекса.

Clone()

Создает копию массива (неполную).

Copy(Array, Array, Int32)*

Копирует данные из одного массива в другой в заданном количестве.

CopyTo(Array, Int32)*

Копирует элементы из текущего массива в заданный, начиная с указанного индекса.

Exists<T>(T[], Predicate<T>)

Определяет наличие элемента удовлетворяющему предикату.

GetValue(Int32)*

Возвращает значение по указанному индексу.

IndexOf(Array, Object)*

Возвращает индекс первого вхождения элемента в массиве.

Reverse(Array)*

Задает обратный порядок для элементов в массиве.

Sort(Array)*

Сортирует элементы массива.

Для вывода содержимого массива в консоль создадим метод PrintArray:

public static void PrintArray<T>(string txt, T[] arr)
{
    Console.Write($"{txt}: ");
    foreach(var v in arr)
    {
        Console.Write($"{v} ");
    }
}

Ниже приведены примеры использования представленных выше методов и свойств класса System.Array:

int[] na6 = {1, 2, 3, 4, 5, 6, 7};
int[,] nm2 = { {1, 2, 3}, {4, 5, 6}};
int[][] nm3 = new int[3][];
for(int i = 0; i < nm3.Length; i++)           
    nm3[i] = new int[i+1];
Console.WriteLine($"na6: Length={na6.Length}, Rank={na6.Rank}"); // na6: Length=7, Rank=1
Console.WriteLine($"nm2: Length={nm2.Length}, Rank={nm2.Rank}"); // nm2: Length=6, Rank=2
Console.WriteLine($"nm3: Length={nm3.Length}, Rank={nm3.Rank}"); // nm3: Length=3, Rank=1
Console.WriteLine("BinarySearch result: " + Array.BinarySearch(na6, 5).ToString()); // BinarySearch result: 4
          
var na7 = (int[])na6.Clone();
Array.Clear(na7, 2, 2);
PrintArray<int>("na6", na6); // na6: 1 2 3 4 5 6 7
PrintArray<int>("na7", na7); // na7: 1 2 0 0 5 6 7
Array.Copy(na7, na6, 4);
PrintArray<int>("na6 after copy", na6); // na6 after copy: 1 2 0 0 5 6 7
(new int[]{1, 2, 3, 4}).CopyTo(na6, 0);
PrintArray<int>("na6", na6); // na6: 1 2 3 4 5 6 7
          
var ans = Array.Exists<int>(na6, v => (v % 2) == 0);
Console.WriteLine($"Is even number exists in na6? Answer: {ans}");
Array.Fill<int>(na7, 7);
PrintArray<int>("na7", na7); // na7: 7 7 7 7 7 7 7
          
Console.WriteLine($"Value at 3 index in na6: {na6.GetValue(3)}");
Console.WriteLine($"Index of value=5 in na6: {Array.IndexOf(na6, 5)}");
Array.Reverse(na6);
PrintArray<int>("na6", na6); // na6: 7 6 5 4 3 2 1
Array.Sort(na6);
PrintArray<int>("na6", na6); // na6: 1 2 3 4 5 6 7

Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.

Поделиться
Share on VK
VK
Tweet about this on Twitter
Twitter
Share on Facebook
Facebook

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *