Инициализирането на масиви в C често е съпроводено с грешки и е досадно. C++ използва инициализация на агрегати за много по-бързо.6 Java няма “агрегати” като C++, понеже всичко е обекти в Java. Има масиви и те се инициализират чрез "инициализация на масиви".
Масивът е просто последователнот от или примитиви или обекти, всички от един тип и опаковани заедно под едно име. Масивите се декларират и използват с оператор от квадратни скоби наречен индексиращ оператор [ ]. За да се дефинира масив просто слагате след типа празни квадратни скоби:
int[] a1;
Може да ги сложите след името със същия резултат:
int a1[];
Това отговаря на очакванията на C и C++ програмистите. Първия пример каточели има по-смислен синтаксис, понеже говори за “масив от int.” Този стил ще се използва в тази книга.
Компилаторът не дава да му казвате колко голям е масивът. Това ни връща пак към въпроса за “манипулаторите.” Всичко което до тук имате е манипулатор към масив и не е алокирана памет за масива. За да се вземе памет за масива трябва да напишете израз за даване на начални стойности. За масивите инициализацията може да се появи навсякъде в програмата, но също може да използвате специален синтаксис в точката, където се декларира масивът. Тази специална инициализация е множество начални стойности, разделени със запетаи и оградени с големи скоби. Алокирането на памет (еквивалентът на използване на new) е обект на грижите на компилатора в този случай. Например:
int[] a1 = { 1, 2, 3, 4, 5 };
Защо въобще ще дефинираме манипулатор на масив без масива?
int[] a2;
Ами възможно е да се присвояват един масив на друг в Java, така че да се напише:
a2 = a1;
Фактически се копира манипулатор, както е демонстрирано тук:
//: c04:Arrays.java
// Arrays of primitives.
public class Arrays {
public static void main(String[] args) {
int[] a1 = { 1, 2, 3, 4, 5 };
int[] a2;
a2 = a1;
for(int i = 0; i < a2.length; i++)
a2[i]++;
for(int i = 0; i < a1.length; i++)
prt("a1[" + i + "] = " + a1[i]);
}
static void prt(String s) {
System.out.println(s);
}
} ///:~
Може да видите че a1 му се дава начална стойност докато на a2 не се дава; a2 му се приравнява по-късно – в този случай към друг масив.
Има нещо ново тука: всички масиви имат вграден член (без значение дали са масиви от примитиви или обекти) който може да четете – но не да променяте – за да видите колко елемента има масивът. Този член е length. Тъй като масивите в Java, подобно на C и C++, започват от елемент нула, най-големият номер на елемент който може да стои като индекс е length - 1. Ако излезете от масива C и C++ тихичко приемат това позволявайки ви да шарите из цялата памет, което е източник на много безславни бъгове. Java обаче ви предпазва от такива неща чрез грешка по време на изпълнение (изключение, тема на глава 9) ако пристъпите извън масива. Разбира се, проверките при всеки достъп до масива струват време и код и не може да се изключат, което значи че достъпът до масивите може да стане източник на неефективност за вашата програма, ако става в критичен момент. За сигурност в Internet и продуктивност на програмиста проектантите на Java са счели че това си струва цената.
Ако не знаете колко елемента ще има масивът докато пишете програмата? Просто използвате new за създаване на елементите в масива. Тук new работи въпреки че се създава масив от примитиви (new не би създал примитив извън масив):
//: c04:ArrayNew.java
// Creating arrays with new.
import java.util.*;
public class ArrayNew {
static Random rand = new Random();
static int pRand(int mod) {
return Math.abs(rand.nextInt()) % mod + 1;
}
public static void main(String[] args) {
int[] a;
a = new int[pRand(20)];
prt("length of a = " + a.length);
for(int i = 0; i < a.length; i++)
prt("a[" + i + "] = " + a[i]);
}
static void prt(String s) {
System.out.println(s);
}
} ///:~
Понеже дължината на масива е случайно избрана (с използване на pRand( ) метода срещан и преди), ясно е че наистина създаването на масива става по време на изпълнение. В добавка се вижда от извежданото от програмата, че елементите се инициализират с ”празни” стойности. (За числата това е нула, за char е null, за boolean е false.)
Разбира се, масивът може да се декларира и инициализира на едно място:
int[] a = new int[pRand(20)];
Ако имате работа с масив от непримитивни обекти, винаги трябва да използвате new. Тук въпросът с манипулаторите се проявява отново понеже това, което фактически се създава е масив от манипулатори. Да вземем обхващащия/заместващия клас Integer, който е клас и не е примитив:
//: c04:ArrayClassObj.java
// Creating an array of non-primitive objects.
import java.util.*;
public class ArrayClassObj {
static Random rand = new Random();
static int pRand(int mod) {
return Math.abs(rand.nextInt()) % mod + 1;
}
public static void main(String[] args) {
Integer[] a = new Integer[pRand(20)];
prt("length of a = " + a.length);
for(int i = 0; i < a.length; i++) {
a[i] = new Integer(pRand(500));
prt("a[" + i + "] = " + a[i]);
}
}
static void prt(String s) {
System.out.println(s);
}
} ///:~
Тука даже след като е извикан new да създаде масива:
Integer[] a = new Integer[pRand(20)];
Това е само масив от манипулатори и докато не се инициализира самия манипулатор чрез създаване на нов Integer обект инициализацията не е завършена:
a[i] = new Integer(pRand(500));
Ако забравите да създадете обекта ще получите грешка по време на изпълнение, когато се опитате да четете празното място на масива (по-точно несъществуващото място на масива, понеже манипулаторът му ще остане инициализиран с "нула", което за манипулатори е "никъде" - бел.прев.).
Хвърлете поглед на формирането на String обект вътре в операторите за извеждане. Може да се види че манипулаторът на Integer обект автоматично се превръща в String представящ стойността в обекта.
Възможно е също да се инициализират масиви от обекти чрез затворен в големи скоби списък. Има две форми, първата от които е единствената позволена в Java 1.0. Втората (еквивалентна) е позволена от Java 1.1 нататък:
//: c04:ArrayInit.java
// Array initialization
public class ArrayInit {
public static void main(String[] args) {
Integer[] a = {
new Integer(1),
new Integer(2),
new Integer(3),
};
// Java 1.1 only:
Integer[] b = new Integer[] {
new Integer(1),
new Integer(2),
new Integer(3),
};
}
} ///:~
Това е полезно понякога, но е по-ограничено понеже дължината на масива се определя по време на компилация. Завършващата запетая в списъка на инициализаторите е незадължителна. (Тази черта помага за поддръжката на дълги списъци.)
Втората форма, въведена в Java 1.1, дава удобен начин да се пишат и викат методи, които произвеждат същия ефект като променливите аргументни списъци (известни още като “varargs”) в C. Това включва неопределен брой аргументи и с неизвестен тип. Тъй като всички класове сигурно са наследници на общ клас Object, може да създадете метод който приема масив от Object и да го викате така:
//: c04:VarArgs.java
// Using the Java 1.1 array syntax to create
// variable argument lists
class A { int i; }
public class VarArgs {
static void f(Object[] x) {
for(int i = 0; i < x.length; i++)
System.out.println(x[i]);
}
public static void main(String[] args) {
f(new Object[] {
new Integer(47), new VarArgs(),
new Float(3.14), new Double(11.11) });
f(new Object[] {"one", "two", "three" });
f(new Object[] {new A(), new A(), new A()});
}
} ///:~
На този етап няма много какво да правите с тези неизвестни обекти и тази програма използва автоматична String конверсия за да направи нещо полезно с Object. В глава 11 (run-time type identification или RTTI) ще научите как да намирате точния тип по време на изпълнение, за да може да правите по-интересни неща.
Многомерни масиви
Java позволява лесно да се създават многомерни масиви:
//: c04:MultiDimArray.java
// Creating multidimensional arrays.
import java.util.*;
public class MultiDimArray {
static Random rand = new Random();
static int pRand(int mod) {
return Math.abs(rand.nextInt()) % mod + 1;
}
public static void main(String[] args) {
int[][] a1 = {
{ 1, 2, 3, },
{ 4, 5, 6, },
};
for(int i = 0; i < a1.length; i++)
for(int j = 0; j < a1[i].length; j++)
prt("a1[" + i + "][" + j +
"] = " + a1[i][j]);
// 3-D array with fixed length:
int[][][] a2 = new int[2][2][4];
for(int i = 0; i < a2.length; i++)
for(int j = 0; j < a2[i].length; j++)
for(int k = 0; k < a2[i][j].length;
k++)
prt("a2[" + i + "][" +
j + "][" + k +
"] = " + a2[i][j][k]);
// 3-D array with varied-length vectors:
int[][][] a3 = new int[pRand(7)][][];
for(int i = 0; i < a3.length; i++) {
a3[i] = new int[pRand(5)][];
for(int j = 0; j < a3[i].length; j++)
a3[i][j] = new int[pRand(5)];
}
for(int i = 0; i < a3.length; i++)
for(int j = 0; j < a3[i].length; j++)
for(int k = 0; k < a3[i][j].length;
k++)
prt("a3[" + i + "][" +
j + "][" + k +
"] = " + a3[i][j][k]);
// Array of non-primitive objects:
Integer[][] a4 = {
{ new Integer(1), new Integer(2)},
{ new Integer(3), new Integer(4)},
{ new Integer(5), new Integer(6)},
};
for(int i = 0; i < a4.length; i++)
for(int j = 0; j < a4[i].length; j++)
prt("a4[" + i + "][" + j +
"] = " + a4[i][j]);
Integer[][] a5;
a5 = new Integer[3][];
for(int i = 0; i < a5.length; i++) {
a5[i] = new Integer[3];
for(int j = 0; j < a5[i].length; j++)
a5[i][j] = new Integer(i*j);
}
for(int i = 0; i < a5.length; i++)
for(int j = 0; j < a5[i].length; j++)
prt("a5[" + i + "][" + j +
"] = " + a5[i][j]);
}
static void prt(String s) {
System.out.println(s);
}
} ///:~
Кодът за извеждането използва length и не зависи от фиксирана дължина на масивите.
Първият пример показва многомерен масив от примитиви. Отделяме всеки вектор с големи скоби:
int[][] a1 = {
{ 1, 2, 3, },
{ 4, 5, 6, },
};
Всяко множество в големи скоби ви придвижва в следващото ниво на масива.
Следващият пример показва тримерен масив, алокиран с new. Тук челият масив се алокира наведнъж:
int[][][] a2 = new int[2][2][4];
Но в третия пример се вижда че всеки вектор в масива, който съставя матрицата, може да бъде с произволна дължина:
int[][][] a3 = new int[pRand(7)][][];
for(int i = 0; i < a3.length; i++) {
a3[i] = new int[pRand(5)][];
for(int j = 0; j < a3[i].length; j++)
a3[i][j] = new int[pRand(5)];
}
Първият new създава масив със първи елемент-случайна дължина и другите неопределени. Вторият new вътре във for цикъла запълва елементите но оставя третият индекс неопределен до третия new.
Ще видите от римера, че масивите се инициализират с нула, ако не ги инициализирате вие.
За масивите от не-примитиви е същото, както е показано в четвъртия пример, демонстриращ възможността да се съберат много new изрази с големи скоби:
Integer[][] a4 = {
{ new Integer(1), new Integer(2)},
{ new Integer(3), new Integer(4)},
{ new Integer(5), new Integer(6)},
};
Петият пример показва как масив от непримитиви може да бъде построен парче по парче:
Integer[][] a5;
a5 = new Integer[3][];
for(int i = 0; i < a5.length; i++) {
a5[i] = new Integer[3];
for(int j = 0; j < a5[i].length; j++)
a5[i][j] = new Integer(i*j);
}
i*j е само за да сложи интересна стойност в Integer.
Сподели с приятели: |