1、数组为什么特殊
数组与其他种类容器之间的区别:效率,类型和保存基本类型的能力。
数组是一种效率最高的存储和随机访问对象引用序列的方式。
优点:元素访问非常快
缺点:为这种速度所付出的的代价是数组对象的大小固定,并且在其生命周期中不可改变。
与ArrayList对比:ArrayList的弹性分配空间需要开销,效率低很多。
与泛型前的容器相比,数组可以持有某种具体类型,这样就可以使用编译器检查防止插入错误类型和抽取不恰当类型。也就是数组可以持有基本类型,而泛型之前的容器则不能。有了泛型:容器则可以检查它们所持有对象的类型,并且有了自动包装机制,容器还能够持有基本类型。
下面的例子将数组与泛型容器进行比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class ContainerComparison { public static void main (String[] args) { BerylliumSphere[] spheres = new BerylliumSphere [10 ]; for (int i = 0 ; i < 5 ; i++) spheres[i] = new BerylliumSphere (); System.out.println(Arrays.toString(spheres)); System.out.println(spheres[4 ]); List<BerylliumSphere> sphereList = new ArrayList <BerylliumSphere>(); for (int i = 0 ; i < 5 ; i++) sphereList.add(new BerylliumSphere ()); System.out.println(sphereList); System.out.println(sphereList.get(4 )); int [] integers = { 0 , 1 , 2 , 3 , 4 , 5 }; System.out.println(Arrays.toString(integers)); System.out.println(integers[4 ]); List<Integer> intList = new ArrayList <Integer>( Arrays.asList(0 , 1 , 2 , 3 , 4 , 5 )); intList.add(97 ); System.out.println(intList); System.out.println(intList.get(4 )); } }
随着容器的出现,数组说过仅存的有点就是效率,如果要解决更一般化的问题,数组可能会受到过多的限制,因此在这些情况下你还是会使用容器。
2、数组是第一级对象
数组标示符其实是一个 引用,指向堆中创建的一个真实对象,可以作为数组初始化语法的一部分隐式的创建此对象,或者用new表达式显示的创建。
只读成员length是唯一一个可以访问的字段或方法
[]是访问数组对象的唯一方式
对象数组和基本类型数组在使用上几乎是相同的,唯一区别是对象数组保存的是引用,基本类型数组直接保存基本类型。
下面总结了初始化数组的各种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class ArrayOptions { public static void main (String[] args) { BerylliumSphere[] a; BerylliumSphere[] b = new BerylliumSphere [5 ]; print("b: " + Arrays.toString(b)); char [] cc = new char [5 ]; print("cc" + Arrays.toString(cc)); BerylliumSphere[] c = new BerylliumSphere [4 ]; for (int i = 0 ; i < c.length; i++) if (c[i] == null ) c[i] = new BerylliumSphere (); BerylliumSphere[] d = { new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere () }; a = new BerylliumSphere []{ new BerylliumSphere (), new BerylliumSphere (), }; print("a.length = " + a.length); print("b.length = " + b.length); print("c.length = " + c.length); print("d.length = " + d.length); a = d; print("a.length = " + a.length); int [] e; int [] f = new int [5 ]; print("f: " + Arrays.toString(f)); int [] g = new int [4 ]; for (int i = 0 ; i < g.length; i++) g[i] = i*i; int [] h = { 11 , 47 , 93 }; print("f.length = " + f.length); print("g.length = " + g.length); print("h.length = " + h.length); e = h; print("e.length = " + e.length); e = new int []{ 1 , 2 }; print("e.length = " + e.length); } }
3、返回一个数组
C或者C++中不能返回一个数组,而只能返回一个指向数组的指针,真会早恒一些问题,使得控制数组的生命周期变得很困难,并且容易造成内存泄露。
Java中,只需要直接返回一个数组即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class IceCream { private static Random rand = new Random (47 ); static final String[] FLAVORS = { "Chocolate" , "Strawberry" , "Vanilla Fudge Swirl" , "Mint Chip" , "Mocha Almond Fudge" , "Rum Raisin" , "Praline Cream" , "Mud Pie" }; public static String[] flavorSet(int n) { if (n > FLAVORS.length) throw new IllegalArgumentException ("Set too big" ); String[] results = new String [n]; boolean [] picked = new boolean [FLAVORS.length]; for (int i = 0 ; i < n; i++) { int t; do t = rand.nextInt(FLAVORS.length); while (picked[t]); results[i] = FLAVORS[t]; picked[t] = true ; } return results; } public static void main (String[] args) { for (int i = 0 ; i < 7 ; i++) System.out.println(Arrays.toString(flavorSet(3 ))); } } public class Chapter16_03 {}
4、多维数组
使用花括号创建多维数组,每对花括号括起来的集合都会把你带到下一级数组:
1 2 3 4 5 6 7 8 9 10 11 class MultidimensionalPrimitiveArray { public static void main (String[] args) { int [][] a = { { 1 , 2 , 3 , }, { 4 , 5 , 6 , }, }; System.out.println(Arrays.deepToString(a)); } }
使用Arrays.deepToString()将多维数组转换为多个String
1 2 3 4 5 6 7 8 9 10 class ThreeDWithNew { public static void main (String[] args) { int [][][] a = new int [2 ][2 ][4 ]; System.out.println(Arrays.deepToString(a)); } }
粗糙数组:数组中构成矩阵的每个向量都可以具有任意的长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class RaggedArray { public static void main (String[] args) { Random rand = new Random (47 ); int [][][] a = new int [rand.nextInt(7 )][][]; for (int i = 0 ; i < a.length; i++) { a[i] = new int [rand.nextInt(5 )][]; for (int j = 0 ; j < a[i].length; j++) a[i][j] = new int [rand.nextInt(5 )]; } System.out.println(Arrays.deepToString(a)); } }
可以用类似的方式处理非基本类型的对象数组,下面使用花括号的方式创建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class MultidimensionalObjectArrays { public static void main (String[] args) { BerylliumSphere[][] spheres = { { new BerylliumSphere (), new BerylliumSphere () }, { new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere () }, { new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere (), new BerylliumSphere () }, }; System.out.println(Arrays.deepToString(spheres)); } }
自动包装机制对数组初始化器也起作用
1 2 3 4 5 6 7 8 9 10 11 12 13 class AutoboxingArrays { public static void main (String[] args) { Integer[][] a = { { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }, { 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 }, { 51 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 }, { 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 , 80 }, }; System.out.println(Arrays.deepToString(a)); } }
也可以逐个地,部分的构建一个非基本类型的对象数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class AssemblingMultidimensionalArrays { public static void main (String[] args) { Integer[][] a; a = new Integer [3 ][]; for (int i = 0 ; i < a.length; i++) { a[i] = new Integer [3 ]; for (int j = 0 ; j < a[i].length; j++) a[i][j] = i * j; } System.out.println(Arrays.deepToString(a)); } }
Arrays.deepToString()方法对基本类型数组和对象数组都起到了作用
5、数组与泛型
通常,数组与泛型不能很好结合,你不能实例化具有参数化类型的数组
擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制保证类型安全。
但是,你可以参数化数组本身的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class ClassParameter <T> { public T[] f(T[] arg) { return arg; } } class MethodParameter { public static <T> T[] f(T[] arg) { return arg; } } class ParameterizedArrayType { public static void main (String[] args) { Integer[] ints = { 1 , 2 , 3 , 4 , 5 }; Double[] doubles = { 1.1 , 2.2 , 3.3 , 4.4 , 5.5 }; Integer[] ints2 = new ClassParameter <Integer>().f(ints); Double[] doubles2 = new ClassParameter <Double>().f(doubles); ints2 = MethodParameter.f(ints); doubles2 = MethodParameter.f(doubles); } }
尽管不能创建实际的持有泛型的数组,但是你可以创建非泛型的数组,然后将其转型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class ArrayOfGenerics { @SuppressWarnings("unchecked") public static void main (String[] args) { List<String>[] ls; List[] la = new List [10 ]; ls = (List<String>[])la; ls[0 ] = new ArrayList <String>(); Object[] objects = ls; objects[1 ] = new ArrayList <Integer>(); List<Integer>[] spheres = (List<Integer>[])new List [10 ]; for (int i = 0 ; i < spheres.length; i++) spheres[i] = new ArrayList <Integer>(); } }
一般而言,泛型在类或方法的边界处很有效,而在类或方法的内部,擦出会使得泛型变得不适用,例如,你不能创建泛型数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class ArrayOfGenericType <T> { T[] array; @SuppressWarnings("unchecked") public ArrayOfGenericType (int size) { array = (T[])new Object [size]; } public T[] rep(){ return array; } }
由于试图创建的类型已被擦除,所以是类型未知的数组,你可以创建Object数组然后将其转型 ,但是会得到一个"不受检查"的警告,因为这个数组没有真正持有或动态检查类型T。
也可以参考泛型章节的擦出的补偿之泛型数组
1 2 3 4 5 6 7 8 public class Chapter16_05 { public static void main (String[] args) { ArrayOfGenericType<Integer> gen = new ArrayOfGenericType <Integer>(2 ); Integer[] intArr = gen.rep(); } }
6、创建测试数据
6.1、Arrays.fill()
只能用同一个 值(同一个对象引用)填充各个位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class FillingArrays { public static void main (String[] args) { int size = 6 ; boolean [] a1 = new boolean [size]; byte [] a2 = new byte [size]; char [] a3 = new char [size]; short [] a4 = new short [size]; int [] a5 = new int [size]; long [] a6 = new long [size]; float [] a7 = new float [size]; double [] a8 = new double [size]; String[] a9 = new String [size]; Arrays.fill(a1, true ); System.out.print("a1 = " + Arrays.toString(a1)); Arrays.fill(a2, (byte )11 ); System.out.print("a2 = " + Arrays.toString(a2)); Arrays.fill(a3, 'x' ); System.out.print("a3 = " + Arrays.toString(a3)); Arrays.fill(a4, (short )17 ); System.out.print("a4 = " + Arrays.toString(a4)); Arrays.fill(a5, 19 ); System.out.print("a5 = " + Arrays.toString(a5)); Arrays.fill(a6, 23 ); System.out.print("a6 = " + Arrays.toString(a6)); Arrays.fill(a7, 29 ); System.out.print("a7 = " + Arrays.toString(a7)); Arrays.fill(a8, 47 ); System.out.print("a8 = " + Arrays.toString(a8)); Arrays.fill(a9, "Hello" ); System.out.print("a9 = " + Arrays.toString(a9)); Arrays.fill(a9, 3 , 5 , "World" ); System.out.print("a9 = " + Arrays.toString(a9)); } }
6.2、数据生成器
下面是各个基本类型的包装类和String类的生成器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 interface Generator <T> { T next () ; } class CountingGenerator { public static class Boolean implements Generator <java.lang.Boolean> { private boolean value = false ; public java.lang.Boolean next () { value = !value; return value; } } public static class Byte implements Generator <java.lang.Byte> { private byte value = 0 ; public java.lang.Byte next () { return value++; } } static char [] chars = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ).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 class Short implements Generator <java.lang.Short> { private short value = 0 ; public java.lang.Short next () { return value++; } } public static class Integer implements Generator <java.lang.Integer> { private int value = 0 ; public java.lang.Integer next () { return value++; } } public static class Long implements Generator <java.lang.Long> { private long value = 0 ; public java.lang.Long next () { return value++; } } public static class Float implements Generator <java.lang.Float> { private float value = 0 ; public java.lang.Float next () { float result = value; value += 1.0 ; return result; } } public static class Double implements Generator <java.lang.Double> { private double value = 0.0 ; public java.lang.Double next () { double result = value; value += 1.0 ; return result; } } }
下面测试一下这些生成器
Tips:使用反射进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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); } }
下面是一组使用随机数生成器的Generator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 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 { { cg = new Character (); } 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 () { 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 ; } } }
6.3、从Generator中创建数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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); } }
下面是CollectionData类的实现
1 2 3 4 5 6 7 8 9 10 class CollectionData <T> extends ArrayList <T> { public CollectionData (Generator<T> gen, int quantity) { for (int i = 0 ; i < quantity; i++) add(gen.next()); } public static <T> CollectionData<T> list (Generator<T> gen, int quantity) { return new CollectionData <T>(gen, quantity); } }
下面使用前一节中的CountingGenerator的一个生成器来测试Generated
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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)); } }
泛型不能用于基本类型,为了填充基本类型的数组,我们需要创建一个转换器,它可以接收任意的包装器对象数组,并将其转换为基本类型数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 class ConvertTo { 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; } public static char [] primitive(Character[] in) { char [] result = new char [in.length]; for (int i = 0 ; i < in.length; i++) result[i] = in[i]; return result; } public static byte [] primitive(Byte[] in) { byte [] result = new byte [in.length]; for (int i = 0 ; i < in.length; i++) result[i] = in[i]; return result; } public static short [] primitive(Short[] in) { short [] result = new short [in.length]; for (int i = 0 ; i < in.length; i++) result[i] = in[i]; return result; } public static int [] primitive(Integer[] in) { int [] result = new int [in.length]; for (int i = 0 ; i < in.length; i++) result[i] = in[i]; return result; } public static long [] primitive(Long[] in) { long [] result = new long [in.length]; for (int i = 0 ; i < in.length; i++) result[i] = in[i]; return result; } public static float [] primitive(Float[] in) { float [] result = new float [in.length]; for (int i = 0 ; i < in.length; i++) result[i] = in[i]; return result; } public static double [] primitive(Double[] in) { double [] result = new double [in.length]; for (int i = 0 ; i < in.length; i++) result[i] = in[i]; return result; } }
最后使用RandomGenerator中的类来测试这些数组生成器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class TestArrayGeneration { public static void main (String[] args) { int size = 6 ; boolean [] a1 = ConvertTo.primitive(Generated.array( Boolean.class, new RandomGenerator .Boolean(), size)); System.out.println("a1 = " + Arrays.toString(a1)); byte [] a2 = ConvertTo.primitive(Generated.array( Byte.class, new RandomGenerator .Byte(), size)); System.out.println("a2 = " + Arrays.toString(a2)); char [] a3 = ConvertTo.primitive(Generated.array(Character.class, new RandomGenerator .Character(), size)); System.out.println("a3 = " + Arrays.toString(a3)); short [] a4 = ConvertTo.primitive(Generated.array( Short.class, new RandomGenerator .Short(), size)); System.out.println("a4 = " + Arrays.toString(a4)); int [] a5 = ConvertTo.primitive(Generated.array( Integer.class, new RandomGenerator .Integer(), size)); System.out.println("a5 = " + Arrays.toString(a5)); long [] a6 = ConvertTo.primitive(Generated.array( Long.class, new RandomGenerator .Long(), size)); System.out.println("a6 = " + Arrays.toString(a6)); float [] a7 = ConvertTo.primitive(Generated.array( Float.class, new RandomGenerator .Float(), size)); System.out.println("a7 = " + Arrays.toString(a7)); double [] a8 = ConvertTo.primitive(Generated.array( Double.class, new RandomGenerator .Double(), size)); System.out.println("a8 = " + Arrays.toString(a8)); } }
7、Arrays实用功能
Arrays类有一套用于数组的静态方法(所有这些方法对各种基本类型和Object类而重载过):
equals()
比较两个数字是否相等
deepEquals()
用于多维数组比较
fill()
填充数组
sort()
数组排序
binarySearch()
在已经排序的数组中查找元素
toString
产生数组的String表示
hashCode()
产生数组的三列吗
Arrays.asList()
接受任意的序列或数组作为其参数,并转换为List容器
7.1、复制数组
System.arraycopy(): 复制数组,比用for循环复制要快很多,该方法对有所类型做了重载,下面是处理int数组的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 ); System.out.println("i = " + Arrays.toString(i)); System.out.println("j = " + Arrays.toString(j)); System.arraycopy(i, 0 , j, 0 , i.length); System.out.println("j = " + Arrays.toString(j)); int [] k = new int [5 ]; Arrays.fill(k, 103 ); System.arraycopy(i, 0 , k, 0 , k.length); System.out.println("k = " + Arrays.toString(k)); Arrays.fill(k, 103 ); System.arraycopy(k, 0 , i, 0 , k.length); System.out.println("i = " + Arrays.toString(i)); Integer[] u = new Integer [10 ]; Integer[] v = new Integer [5 ]; Arrays.fill(u, new Integer (47 )); Arrays.fill(v, new Integer (99 )); System.out.println("u = " + Arrays.toString(u)); System.out.println("v = " + Arrays.toString(v)); System.arraycopy(v, 0 , u, u.length/2 , v.length); System.out.println("u = " + Arrays.toString(u)); } }
注意复制对象数组的时候,只是浅复制。另外,System.arraycopy()不会执行自动包装和自动拆包。
7.2、数组的比较
Arrays.equals(): 相等的条件:元素个数是否相等,对应位置的元素是否也相等。
对于基本类型是通过使用其包装器类的equals()判断是否相等的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class ComparingArrays { public static void main (String[] args) { int [] a1 = new int [10 ]; int [] a2 = new int [10 ]; Arrays.fill(a1, 47 ); Arrays.fill(a2, 47 ); System.out.println(Arrays.equals(a1, a2)); a2[3 ] = 11 ; System.out.println(Arrays.equals(a1, a2)); String[] s1 = new String [4 ]; Arrays.fill(s1, "Hi" ); String[] s2 = { new String ("Hi" ), new String ("Hi" ), new String ("Hi" ), new String ("Hi" ) }; System.out.println(Arrays.equals(s1, s2)); } } class User { private String username; private String nickname; public User (String username, String nickname) { this .username = username; this .nickname = nickname; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getNickname () { return nickname; } public void setNickname (String nickname) { this .nickname = nickname; } } public class Chapter16_07_02 { public static void main (String[] args) { User[] users1 = new User [3 ]; User[] users2 = new User [3 ]; User user1 = new User ("Jason" , "arthinking" ); User user2 = new User ("Jason" , "arthinking" ); Arrays.fill(users1, user1); Arrays.fill(users2, user1); System.out.println(Arrays.equals(users1, users2)); Arrays.fill(users2, user2); System.out.println(Arrays.equals(users1, users2)); } }
7.3、数组元素的比较
下面的类实现了Comparable接口(此接口只有一个compareTo()方法),并且使用Arrays.sort()方法演示了比较的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class CompType implements Comparable <CompType> { 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; } @Override public int compareTo (CompType rv) { 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()); System.out.println("before sorting:" ); System.out.println(Arrays.toString(a)); Arrays.sort(a); System.out.println("after sorting:" ); System.out.println(Arrays.toString(a)); } }
如果没有实现Comparable接口,会抛出ClassCastException运行时异常,因为sort方法会把参数类型转换为Comparable。
Collections的reverseOrder方法可以产生一个Comparator,可以反转自然的排序顺序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Reverse { public static void main (String[] args) { CompType[] a = Generated.array( new CompType [12 ], CompType.generator()); System.out.println("before sorting:" ); System.out.println(Arrays.toString(a)); Arrays.sort(a, Collections.reverseOrder()); System.out.println("after sorting:" ); System.out.println(Arrays.toString(a)); } }
假设有人给你一个并没有实现Comparable的类,或者给你的类实现了Comparable接口,但是你并不喜欢他的实现方式,你需要另外一种不同的比较方式,可以使用策略模式,创建一个实现了Comparator接口的单独的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class CompTypeComparator implements Comparator <CompType> { @Override public int compare (CompType o1, CompType o2) { return (o1.j < o2.j ? -1 : (o1.j == o2.j ? 0 : 1 )); } } class ComparatorTest { public static void main (String[] args) { CompType[] a = Generated.array( new CompType [12 ], CompType.generator()); System.out.println("before sorting:" ); System.out.println(Arrays.toString(a)); Arrays.sort(a, new CompTypeComparator ()); System.out.println("after sorting:" ); System.out.println(Arrays.toString(a)); } }
7.4、数组排序
使用内置的排序方法,可以对任意的基本类型数组排序,也可以对对象数组排序,只要该对象实现了Comparable接口,或者具有相关联的Comparator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class StringSorting { public static void main (String[] args) { String[] sa = Generated.array(new String [20 ], new RandomGenerator .String(5 )); System.out.println("Before sort: " + Arrays.toString(sa)); Arrays.sort(sa); System.out.println("After sort: " + Arrays.toString(sa)); Arrays.sort(sa, Collections.reverseOrder()); System.out.println("Reverse sort: " + Arrays.toString(sa)); Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER); System.out.println("Case-insensitive sort: " + Arrays.toString(sa)); } }
Java标准库中的排序算法针对正排序的特殊类型进行了优化:
针对基本类型的快速排序
针对对象设计的“稳定归并排序”
所以无需担心排序的性能问,除非确实确定了排序不分是程序效率的瓶颈。
7.5、在已排序的数组中查找
如果数组排好序了,可以使用Arrays.binarySearch()进行查找,对未排序的数组使用binarySearch,会产生意想不到的结果。
查找到了则返回大于等于0的整数
否则返回负值,表示保持数组排序状态下此目标所应该插入的位置,此负值计算方法:
\-(插入点) \- 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class ArraySearching { public static void main (String[] args) { Generator<Integer> gen = new RandomGenerator .Integer(1000 ); int [] a = ConvertTo.primitive( Generated.array(new Integer [25 ], gen)); Arrays.sort(a); System.out.println("Sorted array: " + Arrays.toString(a)); while (true ) { int r = gen.next(); int location = Arrays.binarySearch(a, r); if (location >= 0 ) { System.out.println("Location of " + r + " is " + location + ", a[" + location + "] = " + a[location]); break ; } } } }
如果使用Comparator排序对象数组(基本类型数组无法使用Comparator进行排序)了,使用binarySearch对对象数组进行排序的时候必须提供同样的Comparator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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); System.out.println("Index: " + index + "\\n" + sa[index]); } }
summary
现在容器在除了性能外的各个方法都使得数组相形见绌。
有了额外的自动包装机制和泛型,在容器中持有基本类型就变得易如反掌了,而这也进一步促使你使用容器来替换数组。
因为泛型可以产生类型安全的容器,面对这一点,数组变得毫无优势。
优先容器而不是数组,只有在证明性能成为问,并且切花到数组对性能有所帮助时,你才应该将程序重构为使用数组。
如果容器能够像某些语言一样内置于语言的内核中,那么编译器就会得到更好的优化良机。
我们肯定还会使用数组,并且你在读写代码的时候还会看到他,但是,容器几乎是更好的选择。