1. Въведение
В този урок ще се задълбочим в основната концепция на езика Java - масиви.
Първо ще видим какво представлява масив, след това как да ги използваме; като цяло ще разгледаме как да:
- Започнете с масиви
- Четене и записване на елементи от масиви
- Завъртете се над масив
- Трансформирайте масиви в други обекти като Списък или Потоци
- Сортирайте, търсете и комбинирайте масиви
2. Какво е масив?
Първо, първо трябва да дефинираме какво представлява масивът? Според документацията на Java масивът е обект, съдържащ фиксиран брой стойности от същия тип . Елементите на масив се индексират, което означава, че можем да получим достъп до тях с числа (наречени индекси ).
Можем да разглеждаме масив като номериран списък от клетки, като всяка клетка е променлива, съдържаща стойност. В Java номерирането започва от 0.
Има масиви от примитивен тип и масиви от обекти. Това означава, че можем да използваме масиви от int, float, boolean, ... Но също така и масиви от String, Object и потребителски типове.
3. Настройване на масив
Сега, когато масивите са добре дефинирани, нека се потопим в техните обичаи.
Ще покрием много теми, които ни учат как да използваме масиви. Ще научим някои основи като как да декларираме и инициализираме масив, но ще обхванем и по-напреднали теми като сортиране и търсене на масиви.
Нека да започнем първо с декларация и инициализация.
3.1. Декларация
Ще започнем с декларацията. Има два начина за деклариране на масив в Java:
int[] anArray;
или:
int anOtherArray[];
Първият се използва по-широко от втория .
3.2. Инициализация
Сега, когато е време да видим как да инициализираме масиви. Отново има няколко начина за инициализиране на масив. Тук ще видим основните, но тази статия обхваща подробно инициализирането на масиви.
Нека започнем с един прост начин:
int[] anArray = new int[10];
Използвайки този метод, инициализирахме масив от десет int елемента. Имайте предвид, че трябва да посочим размера на масива.
Когато използваме този метод, инициализираме всеки елемент до стойността му по подразбиране , тук 0 . Когато инициализирате масив от Object , елементите са нулеви по подразбиране.
Сега ще видим друг начин, който ни дава възможност да задаваме стойности на масива директно при създаването му:
int[] anArray = new int[] {1, 2, 3, 4, 5};
Тук инициализирахме масив от пет елемента, съдържащ числа от 1 до 5. Когато използваме този метод, не е необходимо да указваме дължината на масива, това е броят на елементите, декларирани след това между скобите.
4. Достъп до елементи
Нека сега видим как да осъществим достъп до елементите на масив. Можем да постигнем това, като изискваме позиция на клетка в масив.
Например, този малък кодов фрагмент ще отпечата 10 на конзолата:
anArray[0] = 10; System.out.println(anArray[0]);
Обърнете внимание как използваме индекси за достъп до клетките на масива. Числото между скобите е конкретната позиция на масива, до който искаме достъп.
При достъп до клетка, ако предаденият индекс е отрицателен или надхвърля последната клетка, Java ще хвърли ArrayIndexOutOfBoundException .
След това трябва да внимаваме да не използваме отрицателен индекс или индекс, по-голям или равен на размера на масива .
5. Итериране над масив
Достъпът до елементи един по един може да бъде полезен, но може да искаме да итерираме през масив. Нека да видим как можем да постигнем това.
Първият начин е да се използва цикълът for :
int[] anArray = new int[] {1, 2, 3, 4, 5}; for (int i = 0; i < anArray.length; i++) { System.out.println(anArray[i]); }
Това трябва да отпечата номера от 1 до 5 на конзолата. Както виждаме, използвахме свойството length . Това е публична собственост, която ни дава размера на масива.
Разбира се, възможно е да се използват и други циклични механизми като while или do while . Но що се отнася до колекциите на Java, възможно е да се придвижвате по масиви, като използвате цикъла foreach :
int[] anArray = new int[] {1, 2, 3, 4, 5}; for (int element : anArray) { System.out.println(element); }
This example is equivalent to the previous one, but we got rid of the indices boilerplate code. The foreach loop is an option when:
- we don't need to modify the array (putting another value in an element won't modify the element in the array)
- we don't need the indices to do something else
6. Varargs
We've already covered the basics when it comes to the creation and manipulation of arrays. Now, we'll dive into more advanced topics, beginning with varargs. As a reminder, varargs are used to pass an arbitrary number of arguments to a method:
void varargsMethod(String... varargs) {}
This method could take from 0 to an arbitrary number of String arguments. An article covering varargs can be found here.
What we have to know here is that inside the method body, a varargs parameter turns into an array. But, we can also pass an array directly as the argument. Let's see how by reusing the example method declared above:
String[] anArray = new String[] {"Milk", "Tomato", "Chips"}; varargsMethod(anArray);
Will behave the same as:
varargsMethod("Milk", "Tomato", "Chips");
7. Transforming an Array into a List
Arrays are great, but sometimes it can be handier to deal with List instead. We'll see here how to transform an array into a List.
We'll first do it the naïve way, by creating an empty list and iterating over the array to add its elements to the list:
int[] anArray = new int[] {1, 2, 3, 4, 5}; List aList = new ArrayList(); for (int element : anArray) { aList.add(element); }
But there is another way, a little bit more succinct:
Integer[] anArray = new Integer[] {1, 2, 3, 4, 5}; List aList = Arrays.asList(anArray);
The static method Arrays.asList takes a varargs argument and creates a list with the passed values. Unfortunately, this method comes with some drawbacks:
- It's not possible to use an array of primitive types
- We can't add or remove elements from the created list, as it'll throw an UnsupportedOperationException
8. From an Array to a Stream
We can now transform arrays into lists, but since Java 8 we have access to the Stream API and we might want to turn our arrays into Stream. Java provides us with the Arrays.stream method for that:
String[] anArray = new String[] {"Milk", "Tomato", "Chips"}; Stream aStream = Arrays.stream(anArray);
When passing an Object array to the method it will return a Stream of the matching type (e.g. Stream for an array of Integer). When passing a primitive one it will return the corresponding primitive Stream.
It's also possible to create the stream only on a subset of the array:
Stream anotherStream = Arrays.stream(anArray, 1, 3);
This will create a Stream with only “Tomato” and “Chips” Strings (the first index being inclusive while the second one is exclusive).
9. Sorting Arrays
Let's now see how to sort an array, that is rearranging its elements in a certain order. The Arrays class provides us with the sort method. A bit like the stream method, sort has a lot of overloadings.
There are overloadings to sort:
- Primitive type arrays: which are sorted in ascending order
- Object arrays (those Object must implement the Comparable interface): which are sorted according to the natural order (relying on the compareTo method from Comparable)
- Generic arrays: which are sorted according to a given Comparator
In addition, it's possible to sort only a specific portion of an array (passing start and end indices to the method).
The algorithms behind the sort method are quick sort and merge sort for primitive and other arrays, respectively.
Let's see how this all work through some examples:
int[] anArray = new int[] {5, 2, 1, 4, 8}; Arrays.sort(anArray); // anArray is now {1, 2, 4, 5, 8} Integer[] anotherArray = new Integer[] {5, 2, 1, 4, 8}; Arrays.sort(anotherArray); // anotherArray is now {1, 2, 4, 5, 8} String[] yetAnotherArray = new String[] {"A", "E", "Z", "B", "C"}; Arrays.sort(yetAnotherArray, 1, 3, Comparator.comparing(String::toString).reversed()); // yetAnotherArray is now {"A", "Z", "E", "B", "C"}
10. Searching in an Array
Searching an array is pretty simple, we can loop over the array and search our element among the array elements:
int[] anArray = new int[] {5, 2, 1, 4, 8}; for (int i = 0; i < anArray.length; i++) { if (anArray[i] == 4) { System.out.println("Found at index " + i); break; } }
Here we searched for number 4 and found it at index 3.
If we have a sorted array though, we can use another solution: the binary search. The principle of binary search is explained in this article.
Fortunately, Java provides us with the Arrays.binarySearch method. We have to give it an array and an element to search.
In case of a generic array, we also have to give it the Comparator that was used to sort the array in the first place. There is again the possibility to call the method on a subset of the array.
Let's see an example of the binary search method usage:
int[] anArray = new int[] {1, 2, 3, 4, 5}; int index = Arrays.binarySearch(anArray, 4); System.out.println("Found at index " + index);
As we stored number 4 in the fourth cell, this will return index 3 as the result. Note that we used an already sorted array.
11. Concatenating Arrays
Finally, let's see how to concatenate two arrays. The idea is to create an array which length is the sum of the two arrays to concatenate. After that we have to add the elements of the first one and then the elements of the second one:
int[] anArray = new int[] {5, 2, 1, 4, 8}; int[] anotherArray = new int[] {10, 4, 9, 11, 2}; int[] resultArray = new int[anArray.length + anotherArray.length]; for (int i = 0; i < resultArray.length; i++) { resultArray[i] = (i < anArray.length ? anArray[i] : anotherArray[i - anArray.length]); }
As we can see, when the index is still lesser than the first array length we add elements from that array. Then we add elements from the second one. We can make use of the Arrays.setAll method to avoid writing a loop:
int[] anArray = new int[] {5, 2, 1, 4, 8}; int[] anotherArray = new int[] {10, 4, 9, 11, 2}; int[] resultArray = new int[anArray.length + anotherArray.length]; Arrays.setAll(resultArray, i -> (i < anArray.length ? anArray[i] : anotherArray[i - anArray.length]));
This method will set all array element according to the given function. This function associates an index with a result.
Ето трета опция за обединяване с масиви: System.arraycopy . Този метод взема масив източник, позиция източник, масив дестинация, позиция дестинация и int, определящ броя на елементите за копиране:
System.arraycopy(anArray, 0, resultArray, 0, anArray.length); System.arraycopy(anotherArray, 0, resultArray, anArray.length, anotherArray.length);
Както виждаме, копираме първия масив, след това втория (след последния елемент на първия).
12. Заключение
В тази подробна статия разгледахме основни и някои усъвършенствани употреби на масиви в Java.
Видяхме, че Java предлага много методи за справяне с масиви чрез помощния клас Arrays . Има и класове на помощни програми за манипулиране на масиви в библиотеки като Apache Commons или Guava.
Пълният код за тази статия може да бъде намерен на нашия GitHub.