Java Language
Массивы
Поиск…
Вступление
Массивы позволяют хранить и извлекать произвольное количество значений. Они аналогичны векторам в математике. Массивы массивов аналогичны матрицам и действуют как многомерные массивы. Массивы могут хранить любые данные любого типа: примитивы, такие как int
или ссылочные типы, такие как Object
.
Синтаксис
-
ArrayType[] myArray;
// Объявление массивов -
ArrayType myArray[];
// Другой допустимый синтаксис (менее часто используемый и обескураженный) -
ArrayType[][][] myArray;
// Объявление многомерных зубчатых массивов (repeat [] s) -
ArrayType myVar = myArray[index];
// Доступ к элементу (считыванию) по индексу -
myArray[index] = value;
// Присвоить значениеindex
позиции массива -
ArrayType[] myArray = new ArrayType[arrayLength];
// Синтаксис инициализации массива -
int[] ints = {1, 2, 3};
// Синтаксис инициализации массива с предоставленными значениями, длина выводится из числа предоставленных значений: {[value1 [, value2] *]} -
new int[]{4, -5, 6} // Can be used as argument, without a local variable
-
int[] ints = new int[3]; // same as {0, 0, 0}
-
int[][] ints = {{1, 2}, {3}, null};
// Инициализация многомерных массивов. int [] extends Object (а также anyType []), поэтому значение null является допустимым.
параметры
параметр | подробности |
---|---|
ArrayType | Тип массива. Это может быть примитивным ( int , long , byte ) или Object ( String , MyObject и т. Д.). |
индекс | Индекс относится к позиции определенного объекта в массиве. |
длина | Каждому массиву при создании требуется заданная длина. Это делается либо при создании пустого массива ( new int[3] ), либо подразумевается при указании значений ( {1, 2, 3} ). |
Создание и инициализация массивов
Основные случаи
int[] numbers1 = new int[3]; // Array for 3 int values, default value is 0
int[] numbers2 = { 1, 2, 3 }; // Array literal of 3 int values
int[] numbers3 = new int[] { 1, 2, 3 }; // Array of 3 int values initialized
int[][] numbers4 = { { 1, 2 }, { 3, 4, 5 } }; // Jagged array literal
int[][] numbers5 = new int[5][]; // Jagged array, one dimension 5 long
int[][] numbers6 = new int[5][4]; // Multidimensional array: 5x4
Массивы могут быть созданы с использованием любого примитивного или ссылочного типа.
float[] boats = new float[5]; // Array of five 32-bit floating point numbers.
double[] header = new double[] { 4.56, 332.267, 7.0, 0.3367, 10.0 };
// Array of five 64-bit floating point numbers.
String[] theory = new String[] { "a", "b", "c" };
// Array of three strings (reference type).
Object[] dArt = new Object[] { new Object(), "We love Stack Overflow.", new Integer(3) };
// Array of three Objects (reference type).
В последнем примере обратите внимание, что подтипы объявленного типа массива разрешены в массиве.
Массивы для пользовательских типов также могут быть построены подобно примитивным типам
UserDefinedClass[] udType = new UserDefinedClass[5];
Массивы, коллекции и потоки
// Parameters require objects, not primitives
// Auto-boxing happening for int 127 here
Integer[] initial = { 127, Integer.valueOf( 42 ) };
List<Integer> toList = Arrays.asList( initial ); // Fixed size!
// Note: Works with all collections
Integer[] fromCollection = toList.toArray( new Integer[toList.size()] );
//Java doesn't allow you to create an array of a parameterized type
List<String>[] list = new ArrayList<String>[2]; // Compilation error!
// Streams - JDK 8+
Stream<Integer> toStream = Arrays.stream( initial );
Integer[] fromStream = toStream.toArray( Integer[]::new );
вступление
Массив - это структура данных, которая содержит фиксированное число примитивных значений или ссылок на экземпляры объектов.
Каждый элемент в массиве называется элементом, и каждый элемент получает доступ к его числовому индексу. Длина массива устанавливается при создании массива:
int size = 42;
int[] array = new int[size];
Размер массива фиксируется во время выполнения при инициализации. Он не может быть изменен после инициализации. Если размер должен быть изменен во время выполнения, вместо него следует использовать класс Collection
такой как ArrayList
. ArrayList
хранит элементы в массиве и поддерживает изменение размера путем выделения нового массива и копирования элементов из старого массива.
Если массив имеет примитивный тип, т. Е.
int[] array1 = { 1,2,3 };
int[] array2 = new int[10];
значения сохраняются в самом массиве. В отсутствие инициализатора (как в array2
выше) значение по умолчанию, присвоенное каждому элементу, равно 0
(ноль).
Если тип массива является ссылкой на объект, как в
SomeClassOrInterface[] array = new SomeClassOrInterface[10];
то массив содержит ссылки на объекты типа SomeClassOrInterface
. Эти ссылки могут ссылаться на экземпляр SomeClassOrInterface
или любого подкласса (для классов) или на реализацию класса (для интерфейсов) SomeClassOrInterface
. Если объявление массива не имеет инициализатора, то каждому элементу присваивается значение по умолчанию null
.
Поскольку все массивы являются int
-индексированными, размер массива должен быть задан с помощью int
. Размер массива не может быть задан как long
:
long size = 23L;
int[] array = new int[size]; // Compile-time error:
// incompatible types: possible lossy conversion from
// long to int
В массивах используется индексная система с нулевым значением , что означает, что индексирование начинается с 0
и заканчивается на length - 1
.
Например, следующее изображение представляет массив с размером 10
. Здесь первый элемент находится в индексе 0
а последний элемент находится в индексе 9
, а первый элемент находится в индексе 1
и последний элемент в индексе 10
(см. Рисунок ниже).
Доступ к элементам массивов осуществляется в постоянное время . Это означает, что доступ к первому элементу массива имеет одинаковую стоимость (по времени) доступа к второму элементу, третьему элементу и так далее.
Java предлагает несколько способов определения и инициализации массивов, включая буквенные и конструкторские обозначения. При объявлении массивов с использованием new Type[length]
каждый элемент будет инициализирован со следующими значениями по умолчанию:
-
0
для примитивных числовых типов :byte
,short
,int
,long
,float
иdouble
. -
'\u0000'
(нулевой символ) для типаchar
. -
false
дляboolean
типа. -
null
для ссылочных типов .
Создание и инициализация массивов примитивных типов
int[] array1 = new int[] { 1, 2, 3 }; // Create an array with new operator and
// array initializer.
int[] array2 = { 1, 2, 3 }; // Shortcut syntax with array initializer.
int[] array3 = new int[3]; // Equivalent to { 0, 0, 0 }
int[] array4 = null; // The array itself is an object, so it
// can be set as null.
При объявлении массива []
будет отображаться как часть типа в начале объявления (после имени типа) или как часть декларатора для определенной переменной (после имени переменной) или обоих:
int array5[]; /* equivalent to */ int[] array5;
int a, b[], c[][]; /* equivalent to */ int a; int[] b; int[][] c;
int[] a, b[]; /* equivalent to */ int[] a; int[][] b;
int a, []b, c[][]; /* Compilation Error, because [] is not part of the type at beginning
of the declaration, rather it is before 'b'. */
// The same rules apply when declaring a method that returns an array:
int foo()[] { ... } /* equivalent to */ int[] foo() { ... }
В следующем примере обе декларации правильны и могут компилироваться и запускаться без каких-либо проблем. Однако как Конвенция по кодированию Java, так и Руководство по стилю Google Java препятствуют форме с помощью скобок после имени переменной - скобки идентифицируют тип массива и должны появляться с обозначением типа . То же самое следует использовать для сигнатур возврата метода.
float array[]; /* and */ int foo()[] { ... } /* are discouraged */
float[] array; /* and */ int[] foo() { ... } /* are encouraged */
Воспринимаемый тип предназначен для размещения переходных пользователей C , которые знакомы с синтаксисом C, который имеет скобки после имени переменной.
В Java возможно иметь массивы размером 0
:
int[] array = new int[0]; // Compiles and runs fine.
int[] array2 = {}; // Equivalent syntax.
Однако, поскольку это пустой массив, никакие элементы не могут быть прочитаны или назначены ему:
array[0] = 1; // Throws java.lang.ArrayIndexOutOfBoundsException.
int i = array2[0]; // Also throws ArrayIndexOutOfBoundsException.
Такие пустые массивы обычно полезны в качестве возвращаемых значений, поэтому вызывающий код должен беспокоиться только о работе с массивом, а не о потенциальном null
значении, которое может привести к исключению NullPointerException
.
Длина массива должна быть неотрицательным целым числом:
int[] array = new int[-1]; // Throws java.lang.NegativeArraySizeException
Размер массива можно определить, используя публичное конечное поле с названием length
:
System.out.println(array.length); // Prints 0 in this case.
Примечание . array.length
возвращает фактический размер массива, а не количество элементов массива, которым было присвоено значение, в отличие от ArrayList.size()
которое возвращает количество элементов массива, которым было присвоено значение.
Создание и инициализация многомерных массивов
Самый простой способ создания многомерного массива состоит в следующем:
int[][] a = new int[2][3];
Это создаст две три длины int
arrays- а a[0]
и a[1]
. Это очень похоже на классическую инициализацию прямоугольных многомерных массивов в стиле C.
Вы можете создавать и инициализировать одновременно:
int[][] a = { {1, 2}, {3, 4}, {5, 6} };
В отличие от C , где поддерживаются только прямоугольные многомерные массивы, внутренние массивы не должны быть одинаковой длины или даже определены:
int[][] a = { {1}, {2, 3}, null };
Здесь, a[0]
является одной длиной int
массива, в то время как a[1]
представляет собой две длины int
массива и a[2]
является null
. Такие массивы называются зубчатыми массивами или оборванными массивами , то есть массивами массивов. Многомерные массивы в Java реализованы как массивы массивов, т. array[i][j][k]
эквивалентен ((array[i])[j])[k]
. В отличие от C # , синтаксический array[i,j]
не поддерживается в Java.
Представление многомерного массива в Java
Создание и инициализация массивов ссылочных типов
String[] array6 = new String[] { "Laurel", "Hardy" }; // Create an array with new
// operator and array initializer.
String[] array7 = { "Laurel", "Hardy" }; // Shortcut syntax with array
// initializer.
String[] array8 = new String[3]; // { null, null, null }
String[] array9 = null; // null
В дополнение к String
литералам и примитивам, показанным выше, синтаксис ярлыков для инициализации массива также работает с каноническими типами Object
:
Object[] array10 = { new Object(), new Object() };
Поскольку массивы являются ковариантными, массив ссылочного типа может быть инициализирован как массив подкласса, хотя ArrayStoreException
будет ArrayStoreException
, если вы попытаетесь установить элемент в нечто иное, чем String
:
Object[] array11 = new String[] { "foo", "bar", "baz" };
array11[1] = "qux"; // fine
array11[1] = new StringBuilder(); // throws ArrayStoreException
Синтаксис ярлыка не может использоваться для этого, потому что синтаксис ярлыка будет иметь неявный тип Object[]
.
Массив может быть инициализирован нулевыми элементами, используя String[] emptyArray = new String[0]
. Например, массив с нулевой длиной, подобный этому, используется для создания Array
из Collection
когда методу нужен тип среды выполнения объекта.
В обоих примитивных и ссылочных типах пустая инициализация массива (например, String[] array8 = new String[3]
) инициализирует массив значением по умолчанию для каждого типа данных .
Создание и инициализация массивов типичного типа
В родовых классах массивы генерических типов не могут быть инициализированы, например, из-за стирания типа :
public class MyGenericClass<T> {
private T[] a;
public MyGenericClass() {
a = new T[5]; // Compile time error: generic array creation
}
}
Вместо этого они могут быть созданы с использованием одного из следующих способов: (обратите внимание, что они будут генерировать непроверенные предупреждения)
Создав массив
Object
и переведя его в общий тип:a = (T[]) new Object[5];
Это самый простой метод, но поскольку базовый массив по-прежнему имеет тип
Object[]
, этот метод не обеспечивает безопасность типов. Поэтому этот метод создания массива лучше всего использовать только в универсальном классе - не публично публиковаться.Используя
Array.newInstance
с параметром класса:public MyGenericClass(Class<T> clazz) { a = (T[]) Array.newInstance(clazz, 5); }
Здесь класс
T
должен быть явно передан конструктору. Возвращаемый типArray.newInstance
всегда являетсяObject
. Однако этот метод более безопасен, потому что вновь созданный массив всегда имеет типT[]
и поэтому может быть безопасно экстернализирован.
Заполнение массива после инициализации
Arrays.fill()
может использоваться для заполнения массива с тем же значением после инициализации:
Arrays.fill(array8, "abc"); // { "abc", "abc", "abc" }
fill()
также может присваивать значение каждому элементу указанного диапазона массива:
Arrays.fill(array8, 1, 2, "aaa"); // Placing "aaa" from index 1 to 2.
Поскольку Java-версия 8, метод setAll
и ее Concurrent
эквивалент parallelSetAll
, могут использоваться для установки каждого элемента массива на сгенерированные значения. Этим методам передается функция генератора, которая принимает индекс и возвращает желаемое значение для этой позиции.
Следующий пример создает целочисленный массив и устанавливает все его элементы в соответствующее значение индекса:
int[] array = new int[5];
Arrays.setAll(array, i -> i); // The array becomes { 0, 1, 2, 3, 4 }.
Отдельная декларация и инициализация массивов
Значение индекса для элемента массива должно быть целым числом (0, 1, 2, 3, 4, ...) и меньше длины массива (индексы основаны на нуле). В противном случае будет выбрано исключение ArrayIndexOutOfBoundsException :
int[] array9; // Array declaration - uninitialized
array9 = new int[3]; // Initialize array - { 0, 0, 0 }
array9[0] = 10; // Set index 0 value - { 10, 0, 0 }
array9[1] = 20; // Set index 1 value - { 10, 20, 0 }
array9[2] = 30; // Set index 2 value - { 10, 20, 30 }
Массивы не могут быть повторно инициализированы синтаксисом ярлыка инициализатора массива
Невозможно повторно инициализировать массив с помощью синтаксиса ярлыков с инициализатором массива, поскольку инициализатор массива может быть указан только в объявлении поля или объявлении локальной переменной или как часть выражения создания массива.
Тем не менее, можно создать новый массив и назначить его переменной, используемой для ссылки на старый массив. Хотя это приводит к тому, что массив, на который ссылается эта переменная, повторно инициализируется, содержимое переменной представляет собой совершенно новый массив. Для этого new
оператор может использоваться с инициализатором массива и назначается переменной массива:
// First initialization of array
int[] array = new int[] { 1, 2, 3 };
// Prints "1 2 3 ".
for (int i : array) {
System.out.print(i + " ");
}
// Re-initializes array to a new int[] array.
array = new int[] { 4, 5, 6 };
// Prints "4 5 6 ".
for (int i : array) {
System.out.print(i + " ");
}
array = { 1, 2, 3, 4 }; // Compile-time error! Can't re-initialize an array via shortcut
// syntax with array initializer.
Создание массива из коллекции
Два метода в java.util.Collection
создают массив из коллекции:
Object[] toArray()
может использоваться следующим образом:
Set<String> set = new HashSet<String>();
set.add("red");
set.add("blue");
// although set is a Set<String>, toArray() returns an Object[] not a String[]
Object[] objectArray = set.toArray();
<T> T[] toArray(T[] a)
можно использовать следующим образом:
Set<String> set = new HashSet<String>();
set.add("red");
set.add("blue");
// The array does not need to be created up front with the correct size.
// Only the array type matters. (If the size is wrong, a new array will
// be created with the same type.)
String[] stringArray = set.toArray(new String[0]);
// If you supply an array of the same size as collection or bigger, it
// will be populated with collection values and returned (new array
// won't be allocated)
String[] stringArray2 = set.toArray(new String[set.size()]);
Разница между ними больше, чем просто отсутствие нетипизированных или типизированных результатов. Их производительность может также отличаться (подробности см. В этом разделе анализа эффективности ):
-
Object[] toArray()
использует векторизованнуюarraycopy
, которая намного быстрее, чем провереннаяarraycopy
используемая вT[] toArray(T[] a)
. -
T[] toArray(new T[non-zero-size])
должен обнулить массив во время выполнения, аT[] toArray(new T[0])
. Такое избегание заставляет последнего звонить быстрее первого. Подробный анализ здесь: Массивы Мудрости Древних .
Начиная с Java SE 8+, где была введена концепция Stream
, можно использовать Stream
созданный сборкой, для создания нового массива с использованием метода Stream.toArray
.
String[] strings = list.stream().toArray(String[]::new);
Примеры, взятые из двух ответов ( 1 , 2 ) на преобразование 'ArrayList в' String [] 'в Java на переполнение стека.
Массивы для строки
Начиная с Java 1.5 вы можете получить представление String
содержимого указанного массива без итерации по каждому элементу. Просто используйте Arrays.toString(Object[])
или Arrays.deepToString(Object[])
для многомерных массивов:
int[] arr = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]
int[][] arr = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(Arrays.deepToString(arr)); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Arrays.toString()
использует метод Object.toString()
для получения значений String
каждого элемента в массиве, помимо массива примитивного типа, его можно использовать для всех типов массивов. Например:
public class Cat { /* implicitly extends Object */
@Override
public String toString() {
return "CAT!";
}
}
Cat[] arr = { new Cat(), new Cat() };
System.out.println(Arrays.toString(arr)); // [CAT!, CAT!]
Если для класса не существует переопределенного toString()
, тогда будет использоваться унаследованная toString()
из Object
. Обычно выход не очень полезен, например:
public class Dog {
/* implicitly extends Object */
}
Dog[] arr = { new Dog() };
System.out.println(Arrays.toString(arr)); // [Dog@17ed40e0]
Создание списка из массива
Метод Arrays.asList()
может использоваться для возврата List
фиксированного размера, содержащего элементы данного массива. Полученный List
будет иметь тот же тип параметра, что и базовый тип массива.
String[] stringArray = {"foo", "bar", "baz"};
List<String> stringList = Arrays.asList(stringArray);
Примечание . Этот список поддерживается ( представлением ) исходного массива, что означает, что любые изменения в списке изменят массив и наоборот. Однако изменения в списке, который изменит его размер (и, следовательно, длину массива), вызовут исключение.
Чтобы создать копию списка, используйте конструктор java.util.ArrayList
взяв Collection
в качестве аргумента:
String[] stringArray = {"foo", "bar", "baz"};
List<String> stringList = new ArrayList<String>(Arrays.asList(stringArray));
В Java SE 7 и более поздних версиях может использоваться пара угловых скобок <>
(пустой набор аргументов типа), который называется Diamond . Компилятор может определить аргументы типа из контекста. Это означает, что информация о типе может быть опущена при вызове конструктора ArrayList
и будет автоматически выведена во время компиляции. Это называется Type Inference, который является частью Java Generics .
// Using Arrays.asList()
String[] stringArray = {"foo", "bar", "baz"};
List<String> stringList = new ArrayList<>(Arrays.asList(stringArray));
// Using ArrayList.addAll()
String[] stringArray = {"foo", "bar", "baz"};
ArrayList<String> list = new ArrayList<>();
list.addAll(Arrays.asList(stringArray));
// Using Collections.addAll()
String[] stringArray = {"foo", "bar", "baz"};
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, stringArray);
Точка стоит отметить о Даймонд , что она не может быть использована с классами Anonymous .
// Using Streams
int[] ints = {1, 2, 3};
List<Integer> list = Arrays.stream(ints).boxed().collect(Collectors.toList());
String[] stringArray = {"foo", "bar", "baz"};
List<Object> list = Arrays.stream(stringArray).collect(Collectors.toList());
Важные замечания, связанные с использованием метода Arrays.asList ()
Этот метод возвращает
List
, который является экземпляромArrays$ArrayList
(статический внутренний классArrays
), а неjava.util.ArrayList
. ПолученныйList
имеет фиксированный размер. Это означает, что добавление или удаление элементов не поддерживается и будетUnsupportedOperationException
исключениеUnsupportedOperationException
:stringList.add("something"); // throws java.lang.UnsupportedOperationException
Новый
List
можно создать, передавList
с поддержкой массива в конструктор новогоList
. Это создает новую копию данных, которая имеет изменяемый размер и не поддерживается исходным массивом:List<String> modifiableList = new ArrayList<>(Arrays.asList("foo", "bar"));
Вызов
<T> List<T> asList(T... a)
в примитивном массиве, такой какint[]
, приведет к созданиюList<int[]>
, единственным элементом которого является исходный примитивный массив вместо фактических элементов исходного массива.Причиной такого поведения является то, что примитивные типы не могут использоваться вместо параметров типового типа, поэтому весь примитивный массив заменяет параметр типового типа в этом случае. Чтобы преобразовать примитивный массив в
List
, прежде всего, преобразовать примитивный массив в массив соответствующего типа-оболочки (т.Arrays.asList
вInteger[]
вместоint[]
).Поэтому это будет напечатать
false
:int[] arr = {1, 2, 3}; // primitive array of int System.out.println(Arrays.asList(arr).contains(1));
С другой стороны, это будет
true
:Integer[] arr = {1, 2, 3}; // object array of Integer (wrapper for int) System.out.println(Arrays.asList(arr).contains(1));
Это также напечатает
true
, потому что массив будет интерпретироваться какInteger[]
):System.out.println(Arrays.asList(1,2,3).contains(1));
Многомерные и зубчатые массивы
Можно определить массив с более чем одним измерением. Вместо того, чтобы получать доступ, предоставляя единый индекс, доступ к многомерному массиву можно получить, указав индекс для каждого измерения.
Объявление многомерного массива можно сделать, добавив []
для каждого измерения к регулярному объявлению массива. Например, чтобы создать двумерный массив int
, добавьте еще один набор скобок в объявление, например int[][]
. Это продолжается для 3-мерных массивов ( int[][][]
) и т. Д.
Чтобы определить двумерный массив с тремя строками и тремя столбцами:
int rows = 3;
int columns = 3;
int[][] table = new int[rows][columns];
Массив можно индексировать и присваивать ему значения с помощью этой конструкции. Обратите внимание, что неназначенные значения являются значениями по умолчанию для типа массива, в этом случае 0
для int
.
table[0][0] = 0;
table[0][1] = 1;
table[0][2] = 2;
Также возможно одновременное создание измерений и даже создание непрямоугольных массивов. Они чаще всего называются зубчатыми массивами .
int[][] nonRect = new int[4][];
Важно отметить, что, хотя можно определить любой размер массива с зубчатым контуром, должен быть определен предшествующий уровень.
// valid
String[][] employeeGraph = new String[30][];
// invalid
int[][] unshapenMatrix = new int[][10];
// also invalid
int[][][] misshapenGrid = new int[100][][10];
Как многомерные массивы представлены в Java
Источник изображения: http://math.hws.edu/eck/cs124/javanotes3/c8/s5.html
Литературная инициализация массива Jagged
Многомерные массивы и зубчатые массивы также могут быть инициализированы литеральным выражением. Следующее объявляет и заполняет массив 2x3 int
:
int[][] table = {
{1, 2, 3},
{4, 5, 6}
};
Примечание : Subarrays с зубцами также может быть null
. Например, следующий код объявляет и заполняет двумерный массив int
чей первый подмассив имеет значение null
, второй субарейр имеет нулевую длину, третий подмассив имеет одну длину, а последний подмассива представляет собой массив из двух длин:
int[][] table = {
null,
{},
{1},
{1,2}
};
Для многомерного массива можно выделить массивы измерения нижнего уровня по их индексам:
int[][][] arr = new int[3][3][3];
int[][] arr1 = arr[0]; // get first 3x3-dimensional array from arr
int[] arr2 = arr1[0]; // get first 3-dimensional array from arr1
int[] arr3 = arr[0]; // error: cannot convert from int[][] to int[]
ArrayIndexOutOfBoundsException
ArrayIndexOutOfBoundsException
генерируется при доступе к несуществующему индексу массива.
Массивы индексируются с нулевым индексом, поэтому индекс первого элемента равен 0
а индекс последнего элемента - array.length - 1
массива минус 1
(т. array.length - 1
).
Поэтому любой запрос элемента массива индексом i
должен удовлетворять условию 0 <= i < array.length
, иначе будет 0 <= i < array.length
ArrayIndexOutOfBoundsException
.
Следующий код - простой пример, в котором генерируется ArrayIndexOutOfBoundsException
.
String[] people = new String[] { "Carol", "Andy" };
// An array will be created:
// people[0]: "Carol"
// people[1]: "Andy"
// Notice: no item on index 2. Trying to access it triggers the exception:
System.out.println(people[2]); // throws an ArrayIndexOutOfBoundsException.
Выход:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
at your.package.path.method(YourClass.java:15)
Обратите внимание, что незаконный индекс, к которому обращаются, также включен в исключение ( 2
в примере); эта информация может быть полезна, чтобы найти причину исключения.
Чтобы этого избежать, просто проверьте, что индекс находится в пределах массива:
int index = 2;
if (index >= 0 && index < people.length) {
System.out.println(people[index]);
}
Получение длины массива
Массивы - это объекты, которые обеспечивают пространство для хранения до размера элементов указанного типа. Размер массива не может быть изменен после создания массива.
int[] arr1 = new int[0];
int[] arr2 = new int[2];
int[] arr3 = new int[]{1, 2, 3, 4};
int[] arr4 = {1, 2, 3, 4, 5, 6, 7};
int len1 = arr1.length; // 0
int len2 = arr2.length; // 2
int len3 = arr3.length; // 4
int len4 = arr4.length; // 7
Поле length
в массиве сохраняет размер массива. Это final
поле и не может быть изменено.
Этот код показывает разницу между length
массива и количеством объектов, хранящихся в массиве.
public static void main(String[] args) {
Integer arr[] = new Integer[] {1,2,3,null,5,null,7,null,null,null,11,null,13};
int arrayLength = arr.length;
int nonEmptyElementsCount = 0;
for (int i=0; i<arrayLength; i++) {
Integer arrElt = arr[i];
if (arrElt != null) {
nonEmptyElementsCount++;
}
}
System.out.println("Array 'arr' has a length of "+arrayLength+"\n"
+ "and it contains "+nonEmptyElementsCount+" non-empty values");
}
Результат:
Array 'arr' has a length of 13
and it contains 7 non-empty values
Сравнение массивов для равенства
Типы массивов наследуют реализации equals()
(и hashCode()
) из java.lang.Object , поэтому equals()
возвращает true только при сравнении с тем же самым объектом массива. Чтобы сравнить массивы для равенства на основе их значений, используйте java.util.Arrays.equals
, который перегружен для всех типов массивов.
int[] a = new int[]{1, 2, 3};
int[] b = new int[]{1, 2, 3};
System.out.println(a.equals(b)); //prints "false" because a and b refer to different objects
System.out.println(Arrays.equals(a, b)); //prints "true" because the elements of a and b have the same values
Когда тип элемента является ссылочным типом, Arrays.equals()
вызывает equals()
для элементов массива для определения равенства. В частности, если тип элемента сам по себе является типом массива, будет использоваться сравнение идентичности. Чтобы сравнить многомерные массивы для равенства, используйте Arrays.deepEquals()
как Arrays.deepEquals()
ниже:
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3 };
Object[] aObject = { a }; // aObject contains one element
Object[] bObject = { b }; // bObject contains one element
System.out.println(Arrays.equals(aObject, bObject)); // false
System.out.println(Arrays.deepEquals(aObject, bObject));// true
Поскольку наборы и карты используют equals()
и hashCode()
, массивы обычно не полезны в качестве заданных элементов или клавиш карты. Либо оберните их в класс-помощник, который реализует equals()
и hashCode()
в терминах элементов массива или преобразует их в экземпляры List
и сохраняет списки.
Массивы для потока
Преобразование массива объектов в Stream
:
String[] arr = new String[] {"str1", "str2", "str3"};
Stream<String> stream = Arrays.stream(arr);
Преобразование массива примитивов в Stream
с использованием Arrays.stream()
преобразует массив в примитивную специализацию Stream:
int[] intArr = {1, 2, 3};
IntStream intStream = Arrays.stream(intArr);
Вы также можете ограничить Stream
для диапазона элементов в массиве. Начальный индекс является включительным, а конечный индекс - исключительным:
int[] values = {1, 2, 3, 4};
IntStream intStream = Arrays.stream(values, 2, 4);
В классе Stream
появляется метод, аналогичный Arrays.stream()
: Stream.of()
. Разница в том, что Stream.of()
использует параметр varargs, поэтому вы можете написать что-то вроде:
Stream<Integer> intStream = Stream.of(1, 2, 3);
Stream<String> stringStream = Stream.of("1", "2", "3");
Stream<Double> doubleStream = Stream.of(new Double[]{1.0, 2.0});
Итерация по массивам
Вы можете перебирать массивы либо с помощью расширенного цикла for (aka foreach), либо с использованием индексов массива:
int[] array = new int[10];
// using indices: read and write
for (int i = 0; i < array.length; i++) {
array[i] = i;
}
// extended for: read only
for (int e : array) {
System.out.println(e);
}
Здесь стоит отметить, что нет прямого способа использовать Iterator в массиве, но через библиотеку Arrays его можно легко преобразовать в список, чтобы получить объект Iterable
.
Для массивов в коробке используйте Arrays.asList :
Integer[] boxed = {1, 2, 3};
Iterable<Integer> boxedIt = Arrays.asList(boxed); // list-backed iterable
Iterator<Integer> fromBoxed1 = boxedIt.iterator();
Для примитивных массивов (с использованием java 8) используйте потоки (в частности, в этом примере - Arrays.stream -> IntStream ):
int[] primitives = {1, 2, 3};
IntStream primitiveStream = Arrays.stream(primitives); // list-backed iterable
PrimitiveIterator.OfInt fromPrimitive1 = primitiveStream.iterator();
Если вы не можете использовать потоки (без java 8), вы можете использовать goava- библиотеку guava :
Iterable<Integer> fromPrimitive2 = Ints.asList(primitives);
В двумерных массивах или более, обе методики могут быть использованы несколько более сложным образом.
Пример:
int[][] array = new int[10][10];
for (int indexOuter = 0; indexOuter < array.length; indexOuter++) {
for (int indexInner = 0; indexInner < array[indexOuter].length; indexInner++) {
array[indexOuter][indexInner] = indexOuter + indexInner;
}
}
for (int[] numbers : array) {
for (int value : numbers) {
System.out.println(value);
}
}
Невозможно установить Array на любое неравномерное значение без использования цикла, основанного на индексе.
Конечно, вы также можете использовать while
или do-while
циклы при итерации с использованием индексов.
Одно примечание: при использовании индексов массива убедитесь, что индекс находится между 0
и array.length - 1
(оба array.length - 1
). Не делайте жестко закодированные предположения относительно длины массива, иначе вы можете сломать свой код, если длина массива изменится, но ваши жестко закодированные значения не будут.
Пример:
int[] numbers = {1, 2, 3, 4};
public void incrementNumbers() {
// DO THIS :
for (int i = 0; i < numbers.length; i++) {
numbers[i] += 1; //or this: numbers[i] = numbers[i] + 1; or numbers[i]++;
}
// DON'T DO THIS :
for (int i = 0; i < 4; i++) {
numbers[i] += 1;
}
}
Это также лучше, если вы не используете причудливые вычисления для получения индекса, но используете индекс для итерации, и если вам нужны разные значения, подсчитайте их.
Пример:
public void fillArrayWithDoubleIndex(int[] array) {
// DO THIS :
for (int i = 0; i < array.length; i++) {
array[i] = i * 2;
}
// DON'T DO THIS :
int doubleLength = array.length * 2;
for (int i = 0; i < doubleLength; i += 2) {
array[i / 2] = i;
}
}
Доступ к массивам в обратном порядке
int[] array = {0, 1, 1, 2, 3, 5, 8, 13};
for (int i = array.length - 1; i >= 0; i--) {
System.out.println(array[i]);
}
Использование временных массивов для уменьшения повторения кода
Итерация по временному массиву вместо повторения кода может сделать ваш код более чистым. Он может использоваться, когда одна и та же операция выполняется для нескольких переменных.
// we want to print out all of these
String name = "Margaret";
int eyeCount = 16;
double height = 50.2;
int legs = 9;
int arms = 5;
// copy-paste approach:
System.out.println(name);
System.out.println(eyeCount);
System.out.println(height);
System.out.println(legs);
System.out.println(arms);
// temporary array approach:
for(Object attribute : new Object[]{name, eyeCount, height, legs, arms})
System.out.println(attribute);
// using only numbers
for(double number : new double[]{eyeCount, legs, arms, height})
System.out.println(Math.sqrt(number));
Имейте в виду, что этот код не должен использоваться в критически важных средах, поскольку массив создается каждый раз, когда вводится цикл, и что примитивные переменные будут скопированы в массив и, следовательно, не могут быть изменены.
Копирование массивов
Java предоставляет несколько способов копирования массива.
для цикла
int[] a = { 4, 1, 3, 2 };
int[] b = new int[a.length];
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
Обратите внимание, что использование этой опции с массивом Object вместо примитивного массива будет заполнять копию ссылкой на исходное содержимое вместо его копии.
Object.clone ()
Поскольку массивы Object
в Java, вы можете использовать Object.clone()
.
int[] a = { 4, 1, 3, 2 };
int[] b = a.clone(); // [4, 1, 3, 2]
Обратите внимание, что метод Object.clone
для массива выполняет мелкую копию , то есть возвращает ссылку на новый массив, который ссылается на те же элементы, что и исходный массив.
Arrays.copyOf ()
java.util.Arrays
предоставляет простой способ выполнить копию массива в другой. Вот основное использование:
int[] a = {4, 1, 3, 2};
int[] b = Arrays.copyOf(a, a.length); // [4, 1, 3, 2]
Обратите внимание, что Arrays.copyOf
также обеспечивает перегрузку, которая позволяет вам изменять тип массива:
Double[] doubles = { 1.0, 2.0, 3.0 };
Number[] numbers = Arrays.copyOf(doubles, doubles.length, Number[].class);
System.arraycopy ()
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Копирует массив из указанного исходного массива, начиная с указанной позиции, в указанную позицию целевого массива.
Ниже приведен пример использования
int[] a = { 4, 1, 3, 2 };
int[] b = new int[a.length];
System.arraycopy(a, 0, b, 0, a.length); // [4, 1, 3, 2]
Arrays.copyOfRange ()
В основном используется для копирования части массива, вы также можете использовать его для копирования целого массива в другой, как показано ниже:
int[] a = { 4, 1, 3, 2 };
int[] b = Arrays.copyOfRange(a, 0, a.length); // [4, 1, 3, 2]
Литейные массивы
Массивы - это объекты, но их тип определяется типом содержащихся объектов. Поэтому нельзя просто отбрасывать A[]
в T[]
, но каждый член A[]
конкретного A[]
должен быть переведен в объект T
Общий пример:
public static <T, A> T[] castArray(T[] target, A[] array) {
for (int i = 0; i < array.length; i++) {
target[i] = (T) array[i];
}
return target;
}
Таким образом, с учетом массива A[]
:
T[] target = new T[array.Length];
target = castArray(target, array);
Java SE предоставляет для этой цели метод Arrays.copyOf(original, newLength, newType)
:
Double[] doubles = { 1.0, 2.0, 3.0 };
Number[] numbers = Arrays.copyOf(doubles, doubles.length, Number[].class);
Удалить элемент из массива
Java не предоставляет прямой метод в java.util.Arrays
для удаления элемента из массива. Для его выполнения вы можете скопировать исходный массив в новый, не удаляя элемент или преобразовывая его в другую структуру, позволяющую удалить.
Использование ArrayList
Вы можете преобразовать массив в java.util.List
, удалить элемент и преобразовать список обратно в массив следующим образом:
String[] array = new String[]{"foo", "bar", "baz"};
List<String> list = new ArrayList<>(Arrays.asList(array));
list.remove("foo");
// Creates a new array with the same size as the list and copies the list
// elements to it.
array = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(array)); //[bar, baz]
Использование System.arraycopy
System.arraycopy()
можно использовать для создания копии исходного массива и удаления System.arraycopy()
элемента. Ниже пример:
int[] array = new int[] { 1, 2, 3, 4 }; // Original array.
int[] result = new int[array.length - 1]; // Array which will contain the result.
int index = 1; // Remove the value "2".
// Copy the elements at the left of the index.
System.arraycopy(array, 0, result, 0, index);
// Copy the elements at the right of the index.
System.arraycopy(array, index + 1, result, index, array.length - index - 1);
System.out.println(Arrays.toString(result)); //[1, 3, 4]
Использование Apache Commons Lang
Чтобы легко удалить элемент, вы можете использовать библиотеку Apache Commons Lang и особенно статический метод removeElement()
класса ArrayUtils
. Ниже пример:
int[] array = new int[]{1,2,3,4};
array = ArrayUtils.removeElement(array, 2); //remove first occurrence of 2
System.out.println(Arrays.toString(array)); //[1, 3, 4]
Ковариантность массива
Объектные массивы являются ковариантными, что означает, что так же, как Integer
является подклассом Number
, Integer[]
является подклассом Number[]
. Это может показаться интуитивным, но может привести к неожиданному поведению:
Integer[] integerArray = {1, 2, 3};
Number[] numberArray = integerArray; // valid
Number firstElement = numberArray[0]; // valid
numberArray[0] = 4L; // throws ArrayStoreException at runtime
Хотя Integer[]
является подклассом Number[]
, он может содержать только Integer
s, и попытка назначить элемент Long
выдает исключение во время выполнения.
Обратите внимание, что это поведение уникально для массивов, и его можно избежать, используя вместо этого общий List
:
List<Integer> integerList = Arrays.asList(1, 2, 3);
//List<Number> numberList = integerList; // compile error
List<? extends Number> numberList = integerList;
Number firstElement = numberList.get(0);
//numberList.set(0, 4L); // compile error
Для всех элементов массива не обязательно использовать один и тот же тип, если они являются подклассом типа массива:
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
I[] array10 = new I[] { new A(), new B(), new C() }; // Create an array with new
// operator and array initializer.
I[] array11 = { new A(), new B(), new C() }; // Shortcut syntax with array
// initializer.
I[] array12 = new I[3]; // { null, null, null }
I[] array13 = new A[] { new A(), new A() }; // Works because A implements I.
Object[] array14 = new Object[] { "Hello, World!", 3.14159, 42 }; // Create an array with
// new operator and array initializer.
Object[] array15 = { new A(), 64, "My String" }; // Shortcut syntax
// with array initializer.
Как изменить размер массива?
Простой ответ заключается в том, что вы не можете этого сделать. После создания массива его размер не может быть изменен. Вместо этого массив может быть только «изменен», создав новый массив с соответствующим размером и скопировав элементы из существующего массива в новый.
String[] listOfCities = new String[3]; // array created with size 3.
listOfCities[0] = "New York";
listOfCities[1] = "London";
listOfCities[2] = "Berlin";
Предположим (например), что новый элемент необходимо добавить в массив listOfCities
как указано выше. Для этого вам необходимо:
- создайте новый массив размером 4,
- скопируйте существующие 3 элемента старого массива в новый массив при смещениях 0, 1 и 2 и
- добавьте новый элемент в новый массив со смещением 3.
Существуют различные способы сделать это. До появления Java 6 наиболее кратким способом было:
String[] newArray = new String[listOfCities.length + 1];
System.arraycopy(listOfCities, 0, newArray, 0, listOfCities.length);
newArray[listOfCities.length] = "Sydney";
Начиная с Java 6 методы Arrays.copyOf
и Arrays.copyOfRange
могут сделать это проще:
String[] newArray = Arrays.copyOf(listOfCities, listOfCities.length + 1);
newArray[listOfCities.length] = "Sydney";
Для других способов копирования массива см. Следующий пример. Имейте в виду, что при изменении размера вам понадобится копия массива с другой длиной до оригинала.
Лучшие альтернативы изменению размера массива
Существуют два основных недостатка с изменением размера массива, как описано выше:
- Это неэффективно. Создание массива больше (или меньше) включает в себя копирование многих или всех существующих элементов массива и выделение нового объекта массива. Чем больше массив, тем он дороже.
- Вы должны иметь возможность обновлять любые «живые» переменные, содержащие ссылки на старый массив.
Один из вариантов - создать массив с достаточно большим размером для начала. Это возможно только в том случае, если вы можете точно определить этот размер до выделения массива . Если вы не можете этого сделать, возникает проблема изменения размера массива.
Другой альтернативой является использование класса структуры данных, предоставляемого библиотекой классов Java SE или сторонней библиотекой. Например, структура «коллекций» Java SE предоставляет ряд реализаций API-интерфейсов List
, Set
и Map
с различными свойствами среды выполнения. Класс ArrayList
ближе всего к характеристикам производительности простого массива (например, O (N) lookup, O (1) get и set, O (N) случайная вставка и удаление), обеспечивая при этом более эффективное изменение размера без проблемы с эталонным обновлением.
(Эффективность изменения размера для ArrayList
исходит из стратегии удвоения размера массива поддержки при каждом изменении размера. Для типичного варианта использования это означает, что вы иногда изменяете размер. Когда вы амортизируете за весь срок службы списка, стоимость изменения размера для каждой вставки O(1)
. При изменении размера простого массива может быть использована одна и та же стратегия.)
Поиск элемента в массиве
Существует множество способов найти местоположение значения в массиве. В приведенных ниже примерах показано, что массив является одним из следующих:
String[] strings = new String[] { "A", "B", "C" };
int[] ints = new int[] { 1, 2, 3, 4 };
Кроме того, каждый устанавливает index
или index2
либо индексу требуемого элемента, либо -1
если элемент отсутствует.
Использование Arrays.binarySearch
(только для отсортированных массивов)
int index = Arrays.binarySearch(strings, "A");
int index2 = Arrays.binarySearch(ints, 1);
Использование массива Arrays.asList
(только для не примитивных массивов)
int index = Arrays.asList(strings).indexOf("A");
int index2 = Arrays.asList(ints).indexOf(1); // compilation error
Использование Stream
int index = IntStream.range(0, strings.length)
.filter(i -> "A".equals(strings[i]))
.findFirst()
.orElse(-1); // If not present, gives us -1.
// Similar for an array of primitives
Линейный поиск с использованием цикла
int index = -1;
for (int i = 0; i < array.length; i++) {
if ("A".equals(array[i])) {
index = i;
break;
}
}
// Similar for an array of primitives
Линейный поиск с использованием сторонних библиотек, таких как org.apache.commons
int index = org.apache.commons.lang3.ArrayUtils.contains(strings, "A");
int index2 = org.apache.commons.lang3.ArrayUtils.contains(ints, 1);
Примечание. Использование прямого линейного поиска более эффективно, чем перенос в список.
Тестирование, если массив содержит элемент
Вышеприведенные примеры могут быть адаптированы для проверки того, содержит ли массив элемент, просто проверяя, вычисляется ли вычисляемый индекс больше или равен нулю.
Кроме того, есть также несколько более сжатых вариантов:
boolean isPresent = Arrays.asList(strings).contains("A");
boolean isPresent = Stream<String>.of(strings).anyMatch(x -> "A".equals(x));
boolean isPresent = false;
for (String s : strings) {
if ("A".equals(s)) {
isPresent = true;
break;
}
}
boolean isPresent = org.apache.commons.lang3.ArrayUtils.contains(ints, 4);
Сортировка массивов
Сортировка массивов можно легко сделать с Массивы апи.
import java.util.Arrays;
// creating an array with integers
int[] array = {7, 4, 2, 1, 19};
// this is the sorting part just one function ready to be used
Arrays.sort(array);
// prints [1, 2, 4, 7, 19]
System.out.println(Arrays.toString(array));
Сортировка массивов строк:
String
- это не числовые данные, она определяет ее собственный порядок, который называется лексикографическим порядком, также известным как алфавитный порядок. Когда вы сортируете массив String с помощью метода sort()
, он сортирует массив в естественный порядок, определенный интерфейсом Comparable, как показано ниже:
Увеличение порядка
String[] names = {"John", "Steve", "Shane", "Adam", "Ben"};
System.out.println("String array before sorting : " + Arrays.toString(names));
Arrays.sort(names);
System.out.println("String array after sorting in ascending order : " + Arrays.toString(names));
Выход:
String array before sorting : [John, Steve, Shane, Adam, Ben]
String array after sorting in ascending order : [Adam, Ben, John, Shane, Steve]
Уменьшение порядка
Arrays.sort(names, 0, names.length, Collections.reverseOrder());
System.out.println("String array after sorting in descending order : " + Arrays.toString(names));
Выход:
String array after sorting in descending order : [Steve, Shane, John, Ben, Adam]
Сортировка массива объектов
Чтобы отсортировать массив объектов, все элементы должны реализовать интерфейс Comparable
или Comparator
для определения порядка сортировки.
Мы можем использовать любой метод sort(Object[])
для сортировки массива объектов в его естественном порядке, но вы должны убедиться, что все элементы в массиве должны реализовать Comparable
.
Кроме того, они должны быть взаимно сопоставимыми, например, e1.compareTo(e2)
не должен бросать ClassCastException
для любых элементов e1 и e2 в массиве. В качестве альтернативы вы можете отсортировать массив объектов в пользовательском порядке, используя метод sort(T[], Comparator)
как показано в следующем примере.
// How to Sort Object Array in Java using Comparator and Comparable
Course[] courses = new Course[4];
courses[0] = new Course(101, "Java", 200);
courses[1] = new Course(201, "Ruby", 300);
courses[2] = new Course(301, "Python", 400);
courses[3] = new Course(401, "Scala", 500);
System.out.println("Object array before sorting : " + Arrays.toString(courses));
Arrays.sort(courses);
System.out.println("Object array after sorting in natural order : " + Arrays.toString(courses));
Arrays.sort(courses, new Course.PriceComparator());
System.out.println("Object array after sorting by price : " + Arrays.toString(courses));
Arrays.sort(courses, new Course.NameComparator());
System.out.println("Object array after sorting by name : " + Arrays.toString(courses));
Выход:
Object array before sorting : [#101 Java@200 , #201 Ruby@300 , #301 Python@400 , #401 Scala@500 ]
Object array after sorting in natural order : [#101 Java@200 , #201 Ruby@300 , #301 Python@400 , #401 Scala@500 ]
Object array after sorting by price : [#101 Java@200 , #201 Ruby@300 , #301 Python@400 , #401 Scala@500 ]
Object array after sorting by name : [#101 Java@200 , #301 Python@400 , #201 Ruby@300 , #401 Scala@500 ]
Преобразование массивов между примитивами и коробочными типами
Иногда требуется преобразование примитивных типов в бокс- типы.
Для преобразования массива можно использовать потоки (в Java 8 и выше):
int[] primitiveArray = {1, 2, 3, 4};
Integer[] boxedArray =
Arrays.stream(primitiveArray).boxed().toArray(Integer[]::new);
С более низкими версиями это может быть путем итерации примитивного массива и явного копирования его в массив в штучной упаковке:
int[] primitiveArray = {1, 2, 3, 4};
Integer[] boxedArray = new Integer[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; ++i) {
boxedArray[i] = primitiveArray[i]; // Each element is autoboxed here
}
Точно так же массив в коробке может быть преобразован в массив его примитивной копии:
Integer[] boxedArray = {1, 2, 3, 4};
int[] primitiveArray =
Arrays.stream(boxedArray).mapToInt(Integer::intValue).toArray();
Integer[] boxedArray = {1, 2, 3, 4};
int[] primitiveArray = new int[boxedArray.length];
for (int i = 0; i < boxedArray.length; ++i) {
primitiveArray[i] = boxedArray[i]; // Each element is outboxed here
}