《Thinking in Java》学习——16章数组

数组为什么这么特殊

1.数组与其他容器之间的区别有三个方面:效率、类型和保存基本类型的能力。在Java中,数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素的访问非常快速。但是为这种速度所付出的 代价是数组对象大小被固定,并且在其生命周期中不可改变。
2.随着自动包装机制的出现,容器已经能与数组几乎一样方便地用于基本类型中了。数组仅存的优点就是效率。然而,如果要解决更加一般化的问题,那数组就可能收到过多的限制,因此在这些情况下还是会使用容器。

数组是第一级对象

1.数组的制度成员length是数组对象的一部分,表示此数组对象可以存储多少元素,也就是说,length是数组的大小,而不是实际保存的元素。因此通过length询问数组的大小有一个缺点:你无法知道数组中确切地有多少个元素。
2.新生成一个数组对象时,其中所有的引用被自动初始化为null,所以检查其中引用是否为null,即可知道数组的某个位置是否存有对象。同样的,如果数组中的对象是基本类型,就会被自动初始化为相应的值。

返回一个数组

1.假设要写一个方法,而且希望它返回不止一个值,而是一组值。在Java中只需要直接“返回一个数组”,无需对数组负责——只要需要它,它就会一直存在,当使用完之后,垃圾回收器会清理掉它。

多维数组

1.Java SE5中的Arrays.deepToString()方法,可以将多维数组转换为多个String

public class ThreeDWithNew {
    public static void main(String... args) {
        int[][][] a = new int[2][2][4];
        System.out.println(Arrays.deepToString(a));
    }
}
/* Output:
[[[0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0]]]
*/

2.数组中构成矩阵的每个向量都可以具有任意长度(这被称为粗糙数组)。

数组与泛型

1.通常数组与泛型不能很好地结合。你不能实例化具有参数化类型的数组。擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制保证类型安全。
2.可以参数化数组本身的类型:

public   T[] f(T[] arg) {
    return arg;
}

3.编译器确实不能让你实例化泛型数组,但是,它允许你创建对这种数组的引用:

List[] ls; //Success
ls = new ArrayList[10]; //Illegal

尽管不能创建实际的持有泛型的数组对象,但是你可以创建非泛型的数组,然后将其转型:

public class ArrayOfGenerics {
    @SupressWarning("unchecked")
    public static void main(String... args) {
        List ls;
        List[] la = new List[10];
        ls = (List[]) la; //unchecked warning
        ls[0] = new ArrayList();
    }
}

问题是数组是协变类型的,因此List[]也是一个Object[],并且你可以利用这一点,将一个ArrayList赋值到你的数组中,而不会有任何编译期或运行时错误。

创建测试数据

一.Arrays.fill()

Java标准类库Arrays有一个作用十分有限的fill()方法:只能用同一个值填充哥哥位置,针对对象而言就是肤质同一个引用进行填充:

int[] a = new int[6];
Arrays.fill(a, 11);

String[] s = new String[6];
Arrays.fill(s, 3, 5,  "Hello");

使用Arrays.fill()可以填充整个数组,或者只填充数组的某个区域。但是由于只能用单一的数值来调用Arrays.fill(),因此所产生的结果并非特别有用。

二.数据生成器

1.为了以灵活的方式创建更有意义的数组,我们可以引用Generator的概念。如果某个工具使用了Generator,那么久就可以通过选择Generator的类型来创建任何类型的数据。

Arrays实用功能

一.复制数组

1.Java标准类库提供有static方法System.arraycopy(),用它复制数组比用for循环要快得多。System.arraycopy()针对所有类型做了重载:

int[] i = new int[7];
int[] j = new int[5];
Arrays.fill(j, 100);
System.arraycopy(i, 0, j, 0, j.length);

arraycopy()需要的参数有:源数组,表示从源数组中的什么位置开始复制的偏移量,表示从目标数组的什么位置开始复制的偏移量,以及需要复制的元素个数。注意避免数组越界操作。
2.System.arraycopy()不会执行自动包装和自动拆包。

二.数组的比较

1.Arrays类提供了重载后的equals()方法,用来比较整个数组。数组相等的条件是元素个数必须相等,并且对应位置的元素也相等,这可以通过对每一个元素使用equals()作比较来判断:

int[] a1 = new int[10];
int[] a2 = new int[10];
Arrays.fill(a1, 10);
Arrays.fill(a2, 10);
print(Arrays.equals(a1, a2)); //Output : true
三.数组元素的比较

1.Java有两种方式来提供比较功能,第一种是实现java.lang.Comparable接口,并实现compareTo()方法,使你的类具有天生的比较能力。第二种是创建一个实现了Comparator接口的类,实现compare()方法,有特殊需要时重写equals()方法。

四.数组排序

1.使用内置的排序方法,就可以对任意的基本类型数组进行排序;也可以对人意的对象数组进行排序,只要该对象实现了Comparable接口或具有相关联的Comparator

String[] sa = {"Java", "C++", "Python"};
Arrays.sort(sa);

Java标准类库中的排序算法针对正排序的特殊类型进行了优化——针对基本类型设计的“快速排序”,以及针对对象设计的“稳定归并排序”。所以无须担心排序的性能。

五.在已排序的数组中查找

1.如果数组已经排好序了,就可以使用Arrays.binarySearch()执行快速查找。如果要对味排序的数组使用,那么就会产生不可预料的结果:

int[] a = {1, 2, 3, 4, 5};
Arrays.sort(a);
int position = Arrays.binarySearch(a, 3);

2.如果找到了目标,Arrays.binarySearch()产生的返回值等于或大于0.否则,它产生负返回值,表示若要保持数组的排序状态此目标元素所应该插入的位置。这个负值的计算方式为:
- (插入点) - 1
插入点是指,第一个大雨查找对象的元素在数组中的位置。
3.搜索算法不是专为包含重复元素的数组而设计的,虽然仍可用,但是无法保证找到的是这些副本中的哪一个。

你可能感兴趣的:(《Thinking in Java》学习——16章数组)