Массивы данных

Массив (array) это объект, хранящий в себе фиксированное количество значений одного типа. Другими словами, массив — это нумерованный набор переменных. Переменная в массиве называется элементом массива, а ее позиция в массиве задается индексом. Если, к примеру, нужно хранить большой объем различных имен, то неудобно для каждого имени создавать отдельную переменную. В этом случае следует использовать массив данных.

Для наглядности приведена картинка с The Java Tutorial.

Массив данных

Индекс начального элемента массива данных равен 0, следующего за ним — 1 и т.д. Индекс последнего элемента в массиве — на единицу меньше размера массива.

В Java массивы данных являются объектами. Это значит, что имя, которое даётся каждому массиву, лишь указывает на адрес какого-то фрагмента данных в памяти. Кроме адреса в этой переменной ничего не хранится. Индекс массива, фактически, указывает на то, насколько надо отступить от начального элемента массива в памяти, чтобы получить доступ к нужному элементу.

Для того чтобы создать массив данных его нужно объявить, т.е. зарезервировать для массива память и инициализировать.

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

При создании массива первым делом его нужно объявить. Пример объявления массива данных целочисленных значений :

int[] firstArray;

Массив данных можно объявить и так :

int secondArray[];

Однако, это не приветствуется соглашением по оформлению кода в Java. Поскольку квадратные скобки обозначают то, что мы имеем дело с массивом данных, то логичнее, чтобы они находились рядом с обозначением типа.

В приведенных выше примерах было объявлено 2 массива с именами firstArray и secondArray. Оба массива содержат элементы типа int.

Подобным образом можно объявить массив любого типа. Пример объявления массива :

byte   [] anArrayOfBytes   ;
short  [] anArrayOfShorts  ;
long   [] anArrayOfLongs   ;
float  [] anArrayOfFloats  ;
double [] anArrayOfDoubles ;
boolean[] anArrayOfBooleans;
char   [] anArrayOfChars   ;
String [] anArrayOfStrings ;
...

Тип массива задается как type[], где type это тип данных содержащихся в нем элементов. Скобки являются специальным обозначением того, что переменные содержатся в массиве. Имя массива может быль любым, однако, оно должно соответствовать правилам именования переменных.

Массивы можно создавать не только из переменных базовых типов, но и из произвольных объектов. При объявлении массива в языке Java не указывается его размер и не резервируется память для него. Происходит лишь создание ссылки на массив.

Резервирование памяти и инициализация массива

Для объявленного массива данных firstArray необходимо зарезервировть память при помощи ключевого слова new. Пример резервирования памяти :

int[] myFirstArray;
myFirstArray = new int[15];

В примере создается массив из 15 элементов типа int и присвоение его переменной firstArray. Объявлять имя массива и резервировать для него память также можно в одной строке.

int[] myArray = new int[10];

При создании массива с помощью ключевого слова new, все элементы массива автоматически инициализированы нулевыми значениями. Для того, чтобы присвоить элементам массива свои начальные значения, необходимо провести его инициализацию. Инициализацию можно проводить поэлементно :

firstArray[0] = 10; // инициализация первого элемента
firstArray[1] = 20; // инициализация второго элемента
firstArray[2] = 30; // и т.д.

Также инициализацию можно проводить в цикле, с помощью индекса проходя все элементы массива и присваивая им значения.

for(int i = 0; i < 15; i++){
    firstArray[i] = 10;
}

Как видно из примеров, для того, чтобы обратиться к элементу массива, нужно указать его имя и в квадратных скобках — индекс элемента. Элемент массива с конкретным индексом ведёт себя также, как и переменная.

Для создания и инициализации массива можно также использовать упрощенную запись. Она не содержит слово new, а в скобках перечисляются начальные значения массива. Примеры объявления массивов с инициализацией :

int[]    a   = new int[10];                   // массив  из 10 элементов типа int
int      n   = 5;
double[] ar1 =  new double[n];                // Массив из 5 элементов double
double[] ar2 = {3.14, 2.71, 0, -2.5, 99.123}; // Массив из 6 элементов типа double

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

Длину любого созданного массива запоминать не обязательно, т.к. имеется свойство, которое его хранит. Обратиться к этому свойству можно дописав .length к имени массива. Например:

int size = ar1.length;

Это свойство нельзя изменять, его можно только читать. Используя это свойство можно писать программный код для обработки массива даже не зная его конкретного размера. Например, так можно вывести на экран элементы любого массива с именем ar2 :

for(int i = 0; i <= ar2.length  - 1; i++) {
  System.out.print(ar2[i] + "  ");
}

Последний элемент массива всегда ar2[ar2.length - 1].

Если программа выйдет за пределы индекса массива, то она будет остановлена с ошибкой ArrayOutOfBoundsException.

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

Массив может состоять не только из элементов какого-то встроенного типа (int, double и пр.), но и, в том числе, из объектов какого-то существующего класса и даже из других массивов. Массив который в качестве своих элементов содержит другие массивы называется многомерным массивом.

Чаще всего используются двумерные массивы. Такие массивы можно легко представить в виде матрицы. Каждая строка которой является обычным одномерным массивом, а объединение всех строк — двумерным массивом в каждом элементе которого хранится ссылка на какую-то строку матрицы.

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

Соответственно, для того, чтобы обратиться к элементу n-мерного массива нужно указать n индексов.

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

int[]         d1; // Одномерный
int[][]       d2; // Двумерный
double[][]    d3; // Трёхмерный
int[][][][][] d5; // Пятимерный

При создании массива можно указать явно размер каждого его уровня :

d2 = int[3][4]; // Матрица  из 3 строк и 4 столбцов

Но можно указать только размер первого уровня:

int[][] dd2 =  int[5][]; /* Матрица из 5 строк. Сколько элементов будет в каждой строке пока не определено. */

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

for(int i=0; i < 5; i++) {
  dd2[i] = new int[i+2];
}

В результате получим массив следующего вида:

0 0
0 0 0
0 0 0 0
0 0 0 0 0
0 0 0 0 0 0

Можно создать массив явно указав его элементы. Например :

int[][] ddd2 = {{1,2}, {1,2,3,4,5}, {1,2,3}};

При этом можно обратиться к элементу с индексом 4 во второй строке ddd2[1][4]. Но если мы обратимся к элементу ddd2[0][4] или ddd2[2][4] — произойдёт ошибка, поскольку таких элементов просто нет. При этом ошибка будет возникать во время исполнения программы, т. е. компилятор её не увидит.

Обычно всё же используются двумерные массивы с равным количеством элементов в каждой строке. Для обработки двумерных массивов используются два вложенных друг в друга цикла с разными счётчиками. Пример заполнения двумерного массива случайными числами от 0 до 9 и вывод его в консоль в виде матрицы:

int[][] da = new int[6][4];
for(int i = 0; i < da.length; i++) {
  for (int j = 0; j < da[i].length; j++) {
    da[i][j] = (int)(Math.random() * 10);
  }
}
for (int i = 0; i < da.length; i++) {
  for (int j = 0; j < da[i].length; j++) {
    System.out.print( da[i][j] + "\t" );
  }
  System.out.println(); // Переход к следующей строке
}

Копирование массива arraycopy

Стандартная библиотека Java содержит статический метод System.arraycopy(), который копирует массивы значительнее быстрее, чем при ручном копировании в цикле for. В аргументах arraycopy() передается исходный массив, начальная позиция копирования в исходном массиве, приёмный массив, начальная позиция копирования в приёмном массиве и количество копируемых элементов. Любое нарушение границ массива приведет к исключению.

Разработчик Avi Yehuda написал программу, которая вычисляет время на копирование с помощью цикла for и с помощью метода arraycopy() на примере с миллионом элементов. Ручное копирование у него заняло 182 мс, с помощью метода arraycopy() - 12 мс. Разница очевидна невооруженным глазом.

public class ArrayCopyTestActivity extends Activity
{
    private static final int SIZE_OF_ARRAY = 1000000;
	private long time;

	/* Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Integer [] sourceArray = new Integer[SIZE_OF_ARRAY];
        Integer [] dst = new Integer[SIZE_OF_ARRAY];
        fillArray(sourceArray);
        
        startBenchmark();
        naiveCopy(sourceArray,dst);
        stopBenchmark();
       
        startBenchmark();
        System.arraycopy(sourceArray, 0, dst, 0, sourceArray.length);
        stopBenchmark();
    }

    private void naiveCopy(Integer [] src, Integer [] dst) {
        for (int i = 0; i < src.length; i++) {
            dst[i]=src[i];
        }
    }

    private void fillArray(Integer [] src) {
        for (int i = 0; i < src.length; i++) {
            src[i]=i;
        }
    }

    private void startBenchmark() {
        time = System.currentTimeMillis();
    }

    private void stopBenchmark() {
        time = System.currentTimeMillis() - time;
        Log.d("array copy test", "time="+time);
    }
}
Наверх
  Рейтинг@Mail.ru