对数组的基本看法是, 你可以创建并组装它们, 通过使用整型索引值访问它们的元素, 并且它们的尺寸不能改变
数组能在编译器检查具体类型, 来防止插入错误类型和抽取不当类型
数组相比于ArrayList, 仅存的优势就是效率, 这两者持有对象都是类型检查的
- 数组对象
数组的标识符只是一个引用, 指向堆中创建的一个真实对象, 这个对象中存储的是指向其他对象的引用
只读成员length是数组对象的唯一成员, []语法是访问数组对象的唯一方式
X[] a; // 未初始化, 不能访问这个引用
X[] b = new X[5]; // b指向一个数组对象, length是5, 表示数组中能够容纳的元素, 但不表示数组中实际保存的元素
// 所有的引用被自动初始化为null
// 如果引用不为null, 就表明这个位置存储了对象, 基本类型创建都有默认的初始值
X[] c = { new X(), new X() }; // {}里面写每一个对象的初始化, 写了几个length就是几, 必须在定义的位置使用
X[] d = new X[] { new X(), new X() }; // 和上面那个类似, 但是除了定义的位置, 也可以用在其他地方, 如方法传参数
a = d; //将指向某个数组对象的引用赋值给另一个数组对象
- 返回一个数组
Java中可以直接返回数组, 和返回一个对象没什么不同
public static String[] f(int n) {
String[] result = new String[n];
return res;
}
- 多维数组
int[][] a = {
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 }
};
多维数组用花括号把每个向量分开
Arrays.deepToString(Object);它可以将多维数组转化为多个String
int[][][] a = new int[2][2][4]; //可以用new来分配多维数组
多维数组必须至少确定第一维的长度, 其他维的可以空着, 当作不确定
这被称为粗糙数组
int[][][] a = new int[2][][];
a[0] = new int[3][];
a[0][0] = new int[5];
包装器对数组初始化器也起作用
Integer[][] a = {
{ 1, 2, 3, 4 },
{ 5, 6, 7 }
}
- 数组与泛型
不能实例化参数化类型的数组
Peel<Banaa>[] peels = new Peel<Banana>[10];
但是可以创建它的引用
List<String>[] ls;
可以创建非泛型数组, 然后将其转型
List<String>[] ls;
List[] la = new ArrayList[];
ls = (List<String>[])la;
ls[0] = new ArrayList<String>();
Object[] objects = ls;
objects[1] = new ArrayList<Integer>();
不能创建泛型数组, 只能通过创建Object[], 再强制转型, 但Object[]在编译期和运行时, 都可以将任意类型对象塞进去
public ArrayOfGenericType(int size) {
array = (T[])new Object[size];
}
- 创建测试数据
fill方法
Arrays.fill(array, value);
Arrays.fill(array, start, end, value);
使用策咯设计模式, 选择Generator来创建数据
使用不同Generator来创建不同基本类型的计数器
static char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
public static class
Character implements Generator<java.lang.Character> {
int index = -1;
public java.lang.Character next() {
index = (index+1)%chars.length;
return chars[index];
}
}
public static class
String implements Generator<java.lang.String> {
private int length = 7;
Generator<java.lang.Character> cg = new Character();
public String() {}
public String(int length) { this.length = length; }
public java.lang.String next() {
char[] buf = new char[length];
for(int i = 0;i < length;i++)
buf[i] = cg.next();
return new java.lang.String(buf);
}
}
可以测试生成器
public static test(Class<?> types) {
for(Class<?> type : types) {
try {
Generator<?> g = (Generator<?>)type.newInstance();
for(int i = 0;i < size;i++)
System.out.print(g.next() + " ");
} catch(Exception e) {}
}
}
用Generator填充数组, 产生Object子类型的数组
public static <T> T[] array(T[] a, Generator<T> gen) {
return new CollectionData<T>(gen, a.length).toArray(a);
}
public static <T> T[] array(Class<T> type, Generator<T> gen, int size) {
T[] a = (T[])java.lang.reflect.Array.newInstance(type, size);
return new CollectionData<T>(gen, a.length).toArray(a);
}
CollectionData类可以创建一个由gen产生元素填充的Collection, 它带有一个toArray方法, 生成一个数组
这里可以创建包装器类型的数组, 还需要一个把包装器数组转化为基本类型数组的重载方法组
public static boolean[] primitive(Boolean in) {
boolean[] result = new boolean[in.length];
for(int i = 0;i < in.length;i++)
result[i] = in[i];
return result;
}
这里自动包装机制, 转化为基本类型的数组
这两个方法组合起来用就可以产生基本类型数组了
boolean[] c = ConvertTo.primitive(
Generated.array(Boolean.class, new CountingGenerator.Boolean(), 7)
);
- 数组的实用方法
System.arraycopy(src, srcPos, dest, destPos, length);
拷贝数组1某位置到数组2某位置开始length个元素
如果拷贝的是对象数组, 拷贝的仅仅是引用, 而不是创建新的对象, 这被称为浅拷贝
Arrays.equals(a1, a2);
静态方法, 比较两个数组是否相等
相等的条件是两个数组的长度相等, 每个位置上的元素相等, 这可以通过每一个元素的equals来对比, 如果是基本类型
可以通过基本类型重载的诸如Integer.equals来对比, 像String也是, 只要求字符串内容相等, 不需要是同一个对象
数组元素的比较
实现java.lang.Comparable接口, 里面有一个compareTo方法, 可以定义比较的方式
class Comptype imlements Comarable<CompType> {
int i;
public Comptype(int i) { this.i = i; }
public int compareTo(ComType rv) {
return ( i < rv.i ? -1 : ( i == rv.i ? 0 : 1 ) );
}
}
Arrays.sort(a);
Collections.reverseOrder()使得原来的排序效果反转
Arrays.sort(a, Collections.reverseOrder());
实现Comparator接口, 可以在sort的参数里面加上自己定义的比较方法
class CompTypeComparator implements Comparator<CompType> {
public int compare(CompType o1, CompType o2) {
return ( o1.j < o2.j ? -1 : ( o1.j == o2.j ? 0 : 1));
}
}
CompType[] a = Generated.array(
new CompType[12], CompType.generator()
);
Arrays.sort(a, new CompTypeComparator());
这里按照j从小到大排序
Arrays.binarySearch(a, r);
在有序数组a里面找元素r的索引, 找不到应插入的位置
如果用Comparator排序了某个对象数组, 在binarySearch中则必须提供相同的Comparator
String[] sa = Generated.array(new String[30],
new RandomGenerator.String(5));
Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
System.out.println(Arrays.toString(sa));
int index = Arrays.binarySearch(sa, sa[10], String.CASE_INSENSITIVE_ORDER);
System.out.println(index + " " + sa[index]);
这里用了String的字典排序方法CASE_INSENSITIVE_ORDER, 二分查找的时候也加入了这个方法
创建数组初始化的时候, 创建的是1个对象, 即数组对象, 像这样 int[] a = new int[5];
它包含的唯一成员就是它的长度length, 所以不管几维数组, 都必须定义第一维的长度, 以产生一个数组对象, 而不是一个空引用
这个对象里面储存的是每一个元素所对应的引用(所以每一个元素的引用可以还没初始化为对象, 抑或也是数组对象)
数组强调的是性能而不是灵活性
应该优先使用容器而不是数组, 除非能证明性能成为问题