《Java编程思想第四版》笔记---16章 数组

16.1 数组为何特殊java

(1)出现泛型以前:算法

数组与其余种类的容器之间的区别有三方面:效率、类型和保存基本类型的能力。设计模式

(2)泛型以后:数组

泛型的出现使得容器也具有了类型检查的能力,而自动装箱机制使容器能够与数组几乎如出一辙的用于基本类型,数组的硕果仅存的优势就是效率。安全

(3)理解数组结构和优势:在Java中数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素访问很是快速。可是为这种速度所付出的代价是数组对象的大小被固定,而且在其生命周期中不可改变。性能


16.2 数组是第一级对象测试

在声明数组时可采用“汇集初始化”方法:优化

int[] integers = {0, 1, 2, 3, 4};spa

可是若是不在声明时初始化则必须采用“动态汇集初始化”方法:设计

int[] integers;

integers = new int[] {0, 1, 2, 3, 4};


16.4 多维数组

Java 1.5新增的Arrays.deepToString()方法能够将多维数组转换为可读的String。

数组中构造矩阵的每一个向量均可以具备任意的长度,这被称为粗糙数组。

自动包装机制对数组初始化器也起做用。


16.5 数组与泛型

Java 不支持泛型数组,不能实例化具备泛型参数类型的数组,但能够建立泛型数组的引用。

List<String>[] ls = new ArrayList<String>[10];

是不支持的,而

List<String>[] ls = new ArrayList[10]

却能够。


提到了一种状况:

List<String>[] lsa = new List<String>[10]; // Not really allowed.

Object o = lsa;

Object[] oa = (Object[]) o;

List<Integer> li = new ArrayList<Integer>();

li.add(new Integer(3));

oa[1] = li; // Unsound, but passes run time store check

String s = lsa[1].get(0); // Run-time error: ClassCastException.


这种状况下,因为JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,因此能够给oa[1]赋上一个ArrayList<Integer>而不会出现ArrayStoreException,可是在取出数据的时候却要作一次类型转换,因此就会出现ClassCastException,若是能够进行泛型数组的声明,上面说的这种状况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。而对泛型数组的声明进行限制,对于这样的状况,能够在编译期提示代码有类型安全问题,比没有任何提示要强不少。


基于以上的缘由,Java不支持声明泛型数组,更确切地表达是:数组的类型不能够是类型变量,除非是采用通配符的方式,看下面这个例子:


List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.

Object o = lsa;

Object[] oa = (Object[]) o;

List<Integer> li = new ArrayList<Integer>();

li.add(new Integer(3));

oa[1] = li; // Correct.

String s = (String) lsa[1].get(0); // Run time error, but cast is explicit.


由于对于通配符的方式,最后取出数据是要作显式的类型转换的,因此并不会存在上一个例子的问题。


16.6 建立测试数据

Java标准类库Arrays有一个做用十分有限的fill()方法,只能用同一个值填充各个位置,针对对象而言,就是复制同一个引用进行填充,还能够只填充数组的某个区域:

Arrays.fill(a9, "Hello");

Arrays.fill(a9, 3, 5, "World");


例子中有一段小数保留两位小数的代码能够借鉴:

int trimmed = Math.round(r.nextFloat() * 100);

return ((float)trimmed) / 100;


16.7 Arrays实用功能

(1) System.arraycopy() 数组复制

Java标准类库提供static方法System.arraycopy(),用它复制数组比用for循环复制快不少。

System.arraycopy(Object src, int srcPosition, Object dest, int destPosition, int length);

注意:System.arraycopy()不会执行自动包装盒自动拆包,两个数组必须具备相同的确切类型。


基本类型数组与对象数组均可以复制。若是复制对象数组,只是复制了对象的引用——而不是对象自己的拷贝,这被称为浅复制

(2) Arrays.equals() 数组比较

Arrays类提供了静态equals()方法,用来比较整个数组。数组相等的条件是元素个数必须相等,而且对应位置的元素也相等,经过对每个元素使用equals()方法来做比较。

使用Arrays.deepEquals()能够比较多维数组。

(3) Arrays.sort() 数组排序

使用静态方法Arrays.sort()用语对数组进行排序。

Java有两种方式来提供比较功能:

第一种:实现java.lang.Comparable接口

此接口只有一个compareTo()方法,此方法接收另外一个Object为参数,若是当前对象小于参数则返回负值,若是相等则返回零,若是当前对象大于参数则返回正值。若是对没有实现Comparable接口的类的数组进行排序,会抛出ClassCastException。由于sort()须要把参数的类型转换为Comparable。

第二种:建立一个实现了Comparator接口的单独的类

假设使用别人定义好的类并无实现Comparable接口,或者类实现了Comparable接口可是须要另一种比较方式。这是策略设计模式的一个应用实例。这个类有两个方法compare()和equals()方法。不必定要实现equals()方法,由于它间接的继承自Object的equals()方法。


Collections类包含一个reverseOrder()静态方法能够产生一个Comparator,它能够翻转天然的排序顺序。

String的排序算法依据词典编排顺序排序,因此大写字母开头的词都放在前面,而后是小写字母。若是想忽略大小写可使用String.CASE_INSENSITIVE_ORDER比较器。


Java标准类库的排序算法对各类类型的正排序都进行了优化——针对基本类型设计的快速排序和针对对象设计的稳定归并排序。因此无需单行排序的性能。

(4) Arrays.binarySearch() 执行快速查找

若是要对未排序的数组使用binarySearch()将产生不可预料的结果(可能指没找到元素的返回状况)。

若是找到了目标,Arrays.binarySearch()产生的返回值大于或等于0,不然产生负返回值。

若要保持数组的排序状态此目标元素应该插入的位置。这个负值的计算方式的: -(插入点)-1。插入点指第一个大于查找对象的元素在数组中的位置,若是数组中全部的元素都小于查找的对象,插入点就等于数组的长度。

若是数组包含重复的元素,则没法保证找到的是这些副本中的哪个。

若是须要对没有重复元素的数组排序可使用TreeSet(保持排序顺序),或者LinkedHashSet(保持插入顺序)。这些类会自动处理全部细节。除非他们成为程序性能的瓶颈,不然不须要本身维护数组。

若是使用Comparator排序了某个对象数组,在使用binarySearch()时必须提供一样的Comparator。


做者在本章的总结中建议,当使用新版本的Java时,优选容器而不是数组。只有在已证实性能成为问题(而且切换到数组对性能提升有所帮助)时,才应该将程序重构为使用数组。