Java编程思想第四版读书笔记——第十六章 数组

第十六章 数组


1、数组为什么特殊

数组的 效率高(唯一优点),它是一个简单的线性序列。
数组之所以优于泛型之前的容器,就是因为可以创建一个树去持有某种基本类型,意味着可以通过 编译期检查,防止插入错误类型和抽取不当类型。

2、数组是第一级对象

对象数组保存的是引用,基本类型数组保存的是值。
数组的初始化:
基本类型:数值型初始化为0,char型初始化为 (char)O,boolean型初始化为false。
对象类型:初始化为null。

3、返回一个数组

直接返回就可以了

4、多维数组

对于多维数组的打印,使用Arrays.deepToString();
数组中构成矩阵的每个向量可以有任意长度,如:
a[][] = {{{}, {0} }, {{0,0}, {0,0,0}, {0,0,0,0,0}}, {{0}} }

5、数组与泛型

数组和泛型不能很好的结合,因为数组必须知道它所持有的确切类型,以强制保证类型安全。
应该首选参数化方法,次选参数化类。更加灵活。

编译器不允许实例化泛型数组,但是可以创建其应用,如:
List<String>[] ls;
然后用非泛型数组转型:
List[] la = new List[10];
ls = (List<String>[])la; // "Unchecked" warning
ls[0] = new ArrayList<String>();

数组是协变类型的。

6、创建测试数据

当在测试时,需要创建大量测试数据来测试程序,这些数据需要自动生成,人为来写不现实,这就是下面的数据生成器的作用。

Arrays.fill()

填充数组,如
double[] a = new double[5];
Arrays.fill(a,1.0);
String[] b = new String[7];
Arrays.fill(b,3,5,"Feynman");

数据生成器

可以实现很多Generator,重载其next()方法:
CountingGenerator类可以生成所有基本类型的包装器类型
import net.mindview.util.*;
public class GeneratorsTest {
public static int size = 10;

public static void test(Class<?> surroundingClass) {
for(Class<?> type : surroundingClass.getClasses()) {
System.out.print(type.getSimpleName() + ": ");
try {
Generator<?> g = (Generator<?>)type.newInstance();
for(int i = 0; i < size; i++)
System.out.printf(g.next() + " ");
System.out.println();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
test(CountingGenerator.class);
}
} /* Output:
Double: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
Float: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
Long: 0 1 2 3 4 5 6 7 8 9
Integer: 0 1 2 3 4 5 6 7 8 9
Short: 0 1 2 3 4 5 6 7 8 9
String: abcdefg hijklmn opqrstu vwxyzAB CDEFGHI JKLMNOP QRSTUVW XYZabcd
efghijk lmnopqr
Character: a b c d e f g h i j
Byte: 0 1 2 3 4 5 6 7 8 9
Boolean: true false true false true false true false true false
*///:~


RandomGenerator类生成随机的基本类型的包装器类型
import java.util.*;
public class RandomGenerator {
private static Random r = new Random(47);
public static class
Boolean implements Generator<java.lang.Boolean> {
public java.lang.Boolean next() {
return r.nextBoolean();
}
}
public static class
Byte implements Generator<java.lang.Byte> {
public java.lang.Byte next() {
return (byte)r.nextInt();
}
}
public static class
Character implements Generator<java.lang.Character> {
public java.lang.Character next() {
return CountingGenerator.chars[
r.nextInt(CountingGenerator.chars.length)];

}
}
public static class
String extends CountingGenerator.String {
// Plug in the random Character generator:
{ cg = new Character(); } // Instance initializer
public String() {}
public String(int length) { super(length); }
}
public static class
Short implements Generator<java.lang.Short> {
public java.lang.Short next() {
return (short)r.nextInt();
}
}
public static class
Integer implements Generator<java.lang.Integer> {
private int mod = 10000;
public Integer() {}
public Integer(int modulo) { mod = modulo; }
public java.lang.Integer next() {
return r.nextInt(mod);
}
}
public static class
Long implements Generator<java.lang.Long> {
private int mod = 10000;
public Long() {}
public Long(int modulo) { mod = modulo; }
public java.lang.Long next() {
return new java.lang.Long(r.nextInt(mod));
}
}
public static class
Float implements Generator<java.lang.Float> {
public java.lang.Float next() {
// Trim all but the first two decimal places:
int trimmed = Math.round(r.nextFloat() * 100);
return ((float)trimmed) / 100;
}
}
public static class
Double implements Generator<java.lang.Double> {
public java.lang.Double next() {
long trimmed = Math.round(r.nextDouble() * 100);
return ((double)trimmed) / 100;
}
}
} ///:~


public class RandomGeneratorsTest {
public static void main(String[] args) {
GeneratorsTest.test(RandomGenerator.class);
}
} /* Output:
Double: 0.73 0.53 0.16 0.19 0.52 0.27 0.26 0.05 0.8 0.76
Float: 0.53 0.16 0.53 0.4 0.49 0.25 0.8 0.11 0.02 0.8
Long: 7674 8804 8950 7826 4322 896 8033 2984 2344 5810
Integer: 8303 3141 7138 6012 9966 8689 7185 6992 5746 3976
Short: 3358 20592 284 26791 12834 -8092 13656 29324 -1423 5327
String: bkInaMe sbtWHkj UrUkZPg wsqPzDy CyRFJQA HxxHvHq XumcXZJ oogoYWM
NvqeuTp nXsgqia
Character: x x E A J J m z M s
Byte: -60 -17 55 -14 -5 115 39 -37 79 115
Boolean: false true false false true true true true true true


从Generator中创建数组

需要两个转换工具,1、使用任意的Generator来产生Object子类型的数组
                                    2、接受任意基本类型的包装器类型数组,并产生相应的基本类型数组

public class Generated {
//方法一
public static <T> T[] array(T[] a, Generator<T> gen) {
return new CollectionData<T>(gen, a.length).toArray(a);
}
// 方法二
@SuppressWarnings("unchecked")
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, size).toArray(a);
}
}
使用上类来创建数组:
import java.util.*;
import net.mindview.util.*;
public class TestGenerated {
public static void main(String[] args) {
Integer[] a = { 9, 8, 7, 6 };
System.out.println(Arrays.toString(a));
a = Generated.array(a,new CountingGenerator.Integer());  //第一类填充
System.out.println(Arrays.toString(a));
Integer[] b = Generated.array(Integer.class,new CountingGenerator.Integer(), 15); //第二类,无中生有
System.out.println(Arrays.toString(b));
}
} /* Output:
[9, 8, 7, 6]
[0, 1, 2, 3]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]


7、Arrays实用功能

六个基本功能
equals() 比较数组是否相等
fill() 填充数据
sort() 排序
binarySearch() 在排序数组中查找
hashCode() 产生散列码
List( ) 转为List容器

复制数组

System.arraycopy(源数组,源数组偏移量,目标数组,目标数组偏移量,需要复制元素个数) 复制数组,效率高

public class CopyingArrays {
public static void main(String[] args) {
int[] i = new int[7];
int[] j = new int[10];
Arrays.fill(i, 47);
Arrays.fill(j, 99);
print("i = " + Arrays.toString(i));
print("j = " + Arrays.toString(j));
System.arraycopy(i, 0, j, 0, i.length);
print("j = " + Arrays.toString(j));
int[] k = new int[5];
Arrays.fill(k, 103);
System.arraycopy(i, 0, k, 0, k.length);
print("k = " + Arrays.toString(k));
Arrays.fill(k, 103);
System.arraycopy(k, 0, i, 0, k.length);
print("i = " + Arrays.toString(i));
// Objects:
Integer[] u = new Integer[10];
Integer[] v = new Integer[5];
Arrays.fill(u, new Integer(47));
Arrays.fill(v, new Integer(99));
print("u = " + Arrays.toString(u));
print("v = " + Arrays.toString(v));
System.arraycopy(v, 0, u, u.length/2, v.length);
print("u = " + Arrays.toString(u));
}
} /* Output:
i = [47, 47, 47, 47, 47, 47, 47]
j = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99]
k = [47, 47, 47, 47, 47]
i = [103, 103, 103, 103, 103, 47, 47]
u = [47, 47, 47, 47, 47, 47, 47, 47, 47, 47]
v = [99, 99, 99, 99, 99]
u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99]
*///:~


对对象的复制是浅复制(只复制元素对象的引用,不拷贝对象本身)

数组比较

想到条件:元素个数相等,元素内容相等。(基于内容的)

数组元素比较

不是讲进行比较的代码写成不同的子程序,而是使用策略设计模式。将“会发生变化的代码”封装在单独的类(策略对象)中,传给相同的代码,使用对象的策略完成其算法。

方法一:实现java.lang.Comparable接口,覆写其compareTo()方法。
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class CompType implements Comparable<CompType> {  //实现Comparable接口
int i;
int j;
private static int count = 1;
public CompType(int n1, int n2) {
i = n1;
j = n2;
}
public String toString() {
String result = "[i = " + i + ", j = " + j + "]";
if(count++ % 3 == 0)
result += "\n";
return result;
}
public int compareTo(CompType rv) {   //实现compareTo方法,按照成员变量i升序
return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));
}
private static Random r = new Random(47);
public static Generator<CompType> generator() {
return new Generator<CompType>() {
public CompType next() {
return new CompType(r.nextInt(100),r.nextInt(100));
}
};
}
public static void main(String[] args) {
CompType[] a =Generated.array(new CompType[12], generator());  //创建一个数组
print("before sorting:");
print(Arrays.toString(a));
Arrays.sort(a);  //排序
print("after sorting:");
print(Arrays.toString(a));
}
} /* Output:
before sorting:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
after sorting:
[[i = 9, j = 78], [i = 11, j = 22], [i = 16, j = 40]
, [i = 20, j = 58], [i = 22, j = 7], [i = 51, j = 89]
, [i = 58, j = 55], [i = 61, j = 29], [i = 68, j = 0]
, [i = 88, j = 28], [i = 93, j = 61], [i = 98, j = 61]
]
*///:~


如果没有实现Comparable接口,调用sort()会抛出ClassCastException异常。

方法二:不可能总改变类的接口或者修改其代码,更灵活的是 需要比较时,创建一个实现Comparable接口的类。
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
public class Reverse {
public static void main(String[] args) {
CompType[] a = Generated.array(new CompType[12], CompType.generator());
print("before sorting:");
print(Arrays.toString(a));
Arrays.sort(a, Collections.reverseOrder());  //反转排序
print("after sorting:");
print(Arrays.toString(a));
}
} /* Output:
before sorting:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
after sorting:
[[i = 98, j = 61], [i = 93, j = 61], [i = 88, j = 28]
, [i = 68, j = 0], [i = 61, j = 29], [i = 58, j = 55]
, [i = 51, j = 89], [i = 22, j = 7], [i = 20, j = 58]
, [i = 16, j = 40], [i = 11, j = 22], [i = 9, j = 78]
]
*///:~


不用系统自带的Collections.reverseOrder(),就自己写个Comparator:
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
class CompTypeComparator implements Comparator<CompType> {
public int compare(CompType o1, CompType o2) {
return (o1.j < o2.j ? -1 : (o1.j == o2.j ? 0 : 1));
}
}
public class ComparatorTest {

public static void main(String[] args) {
CompType[] a = Generated.array(new CompType[12], CompType.generator());
print("before sorting:");
print(Arrays.toString(a));
Arrays.sort(a, new CompTypeComparator());
print("after sorting:");
print(Arrays.toString(a));
}
} /* Output:
before sorting:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
after sorting:
[[i = 68, j = 0], [i = 22, j = 7], [i = 11, j = 22]
, [i = 88, j = 28], [i = 61, j = 29], [i = 16, j = 40]
, [i = 58, j = 55], [i = 20, j = 58], [i = 93, j = 61]
, [i = 98, j = 61], [i = 9, j = 78], [i = 51, j = 89]
]


数组排序

基本数据类型内置了排序方法,如String排序是按照词典编排顺序排列,大写字母在前,小写字母在后。如果想一起排序,就 String.CASE_INSENSITIVE_ORDER。
如下:
Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);

在已排序数组中查找

对于已经排序好的数组,使用Arrays.binarySearch()快速查找,必须是对排序好的数组才可使用。如果在数组中找到要找的值,返回该值所在位置的索引,如果未找到,则返回
负值,其计算方式是 -(插入点)-1,插入点指第一个大于该元素的数所在的位置。
如 对数组{1,2,4,5}查找3,那么返回 - 2-1 = -3

如果对没有重复元素的数组排序,可以使用TreeSet或者LinkedHashSet。
如果使用了Comarator排序某个对象数组(基本类型数组无法使用Comparator排序,只能直接使用或者加个String忽略大小写的参数),在使用binarySearch()时必须提供同样的Comparator.如:
import java.util.*;
import net.mindview.util.*;
public class AlphabeticSearch {
public static void main(String[] args) {
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); //使用相同Comparator
System.out.println("Index: "+ index + "\n"+ sa[index]);
}
} /* Output:
[bkIna, cQrGs, cXZJo, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa, HqXum,
HxxHv, JMRoE, JmzMs, Mesbt, MNvqe, nyGcF, ogoYW, OneOE, OWZnT, RFJQA,
rUkZP, sgqia, slJrL, suEcU, uTpnX, vpfFv, WHkjU, xxEAJ, YNzbr, zDyCy]
Index: 10
HxxHv
*///:~





版权声明:本文为severusyue原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。