数组
- 数组我相信大家都很熟悉吧。数组的优点就是随机读取速度快,像ArrayList就是使用了数组作为数据存储格式而实现的。但是缺点也很明显,它的大小是固定的。ArrayList之所以能够任意大小,是因为使用了size,扩容的时候拷贝数组到新数组。本章我们就来讨论一下数组的特点和用法。
数组是第一级对象
- 无论使用哪种类型的数组,数组标识符其实只是一个引用,指向堆中创建的一个真实对象,这个(数组)对象用以保存指向其他对象的引用。除了new表达式显式创建数组对象,还可以用一些隐式语法。length是它的唯一一个可以访问的成员属性,并且是只读的。数组的类型既可以是引用类型,也可以是基本类型。如果是基本类型的话,都会有默认值。而引用类型则为null。
多维数组
- 我们这里来说一下粗糙数组。通常我们会这么定义多维数组:
int[][] test = new int[5][5];
- 这样相当于是定义了一个5*5的矩阵。但是如果说,我想定义一个不规则大小的数组怎么办?那么我们就可以这么写:
int[][] test = new int[5][];
test[0] = new int[1];
test[1] = new int[2];
test[2] = new int[3];
test[3] = new int[4];
test[4] = new int[5];
- java允许我们先确定最外层数组的大小,然后再逐步确定里层数组大小。这种写法比较少用,这里就做个笔记。
数组和泛型
- 通常,数组与泛型不能很好的结合。你不能实例化具有参数化类型的数组:
List<String>[] ls = new List<String>[10];
- 但是我们可以通过一些人为控制来完成这个数组的创建。我们先来回忆一下泛型的使用。
public class People {
public static void main(String args[]) {
Holder<Integer> ho1 = new Holder<Integer>();
Holder<String> ho2 = new Holder<String>();
Holder ho11 = ho1;
Holder ho22 = ho2;
Holder<String> ho111 = ho11;
Holder<Integer> ho222 = ho22;
ho111.set(new String("1"));
ho222.set(new Integer(1));
System.out.println(ho111.get());
System.out.println(ho222.get());
}
}
class Holder <T> {
T obj;
T get() {
return obj;
}
void set(T obj) {
this.obj = obj;
}
}
----------------运行结果
1
1
- 你也许会以为
new Holder<Integer>()
和new Holder<String>()
是两种不同的类型。但是事实告诉我们,我们试图往前者插入String,不会导致任何错误。这是为什么呢?因为在Holder内部,它只认为泛型是它的擦除边界Object而已。而真正能够限制我们的是ho111认为的泛型类型(在编译期间)。我们当然不会这么写代码,此处也是为了让我们深刻的理解泛型的作用。再次强调,在泛型类内部,所有的泛型类型是擦除边界! - 那么此处我们应该就这样去定义这个数组:
List<String>[] ls = new List[10];
- 你不要觉得
new List[10];
相比于new List<String>[10];
有什么区别,他们本质是没有区别的,这个在上面已经讨论过了。只要我们List<String>[] ls
这么做了,编译器就能够为我们有效检查ls元素的类型。虽然会有警告,但是大家可以放心的这么写。
Arrays实用功能
- 先来讲一下Arrays.equals()和Arrays.deepEquals()。前者是针对一维数组的,会比较其每个元素的equals()方法,后者是针对多维数组的。
import java.util.Arrays;
public class People {
public static void main(String args[]) throws Exception {
int[] a = new int[10];
int[] b = new int[10];
System.out.println(Arrays.equals(a, b));
a[0] = 1;
System.out.println(Arrays.equals(a, b));
int[][] c = new int[1][];
int[][] d = new int[1][];
a = new int[1];
b = new int[1];
c[0] = a;
d[0] = a;
System.out.println(Arrays.equals(c, d));
System.out.println(Arrays.deepEquals(c, d));
d[0] = b;
System.out.println(Arrays.equals(c, d));
System.out.println(Arrays.deepEquals(c, d));
System.out.println();
c[0][0] = 1;
System.out.println(Arrays.equals(c, d));
System.out.println(Arrays.deepEquals(c, d));
}
}
- sort()是用于排序的,binarySearch()是对已经有序的数组进行二分法查询元素。toString()产生数组的字符串表示,fill()用于填充单一元素内容。还有其他用法就不过多介绍了。
复制数组
- System.arraycopy()是一个用于复制(浅拷贝)数组的方法,它的速度比用循环复制要快得多。它需要的参数有:源数组,源数组偏移量,目标数组,目标数组偏移量,复制个数。另外需要注意的是该方法不会执行自动包装和自动拆包,两个数组必须具有相同的确切类型。来看下面的例子:
import java.lang.reflect.Field
import java.util.Arrays
public class People {
public static void main(String args[]) throws Exception {
int[] a = new int[10]
int[] b = new int[10]
Arrays.fill(a, 1)
Arrays.fill(a, 2)
System.out.println(Arrays.toString(a))
System.out.println(Arrays.toString(b))
//System.arraycopy(a, 5, b, 0, b.length)
//System.arraycopy(a, 0, b, 5, b.length)
System.arraycopy(a, 5, b, 5, b.length - 5)
System.out.println(Arrays.toString(b))
Integer[] c = new Integer[10]
Arrays.fill(c, new Integer(3))
System.out.println(Arrays.toString(c))
//System.arraycopy(a, 5, c, 5, c.length - 5)
Integer[] d = new Integer[10]
Arrays.fill(d, new Integer(4))
//这种写法针对下面的浅拷贝测试只改变d[0]
System.arraycopy(d, 0, c, 5, d.length - 5)
System.out.println(Arrays.toString(c))
//为了验证是浅拷贝:
//接下来我打算用反射改变d[0]的值,当然也可以使用其他引用类型做测试,我这里就不改了
Field f = Integer.class.getDeclaredField("value")
f.setAccessible(true)
f.set(d[0], 520)
System.out.println(Arrays.toString(d))
System.out.println(Arrays.toString(c))
//更简单的判断方式:
System.out.println(new Integer(520) == new Integer(520))
System.out.println(d[0] == c[5])
}
}
----------------------------运行结果
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 2, 2, 2, 2, 2]
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
[3, 3, 3, 3, 3, 4, 4, 4, 4, 4]
[520, 520, 520, 520, 520, 520, 520, 520, 520, 520]
[3, 3, 3, 3, 3, 520, 520, 520, 520, 520]
sort
- Arrays.sort是一个很常用的方法,直接对int排序或者String排序,我相信大家都会,那如果我要对一个自定义的数组排序,该如何呢?此时我们需要使这个类实现Comparable接口,否则直接调用sort会出现CCE异常(因为其内部使用了Comparable接口的方法)。
import java.util.Arrays;
import java.util.Comparator;
public class People {
public static void main(String args[]) throws Exception {
Test[] test = new Test[5];
for (int i = 0; i < test.length; i++) {
test[i] = new Test(i, i + "" + i);
}
System.out.println(Arrays.toString(test));
Arrays.sort(test);
System.out.println(Arrays.toString(test));
Arrays.sort(test, new Comparator<Test>() {
public int compare(Test o1, Test o2) {
return o1.s.compareTo(o2.s);
}
});
System.out.println(Arrays.toString(test));
}
}
class Test implements Comparable<Test> {
int i;
String s;
public Test(int i, String s) {
this.i = i;
this.s = s;
}
public int compareTo(Test o) {
return o.i - i;
}
public String toString() {
return i + "->" + s;
}
}
-------------------执行结果:
[0->00, 1->11, 2->22, 3->33, 4->44]
[4->44, 3->33, 2->22, 1->11, 0->00]
[0->00, 1->11, 2->22, 3->33, 4->44]