Java编程思想 第十六章:数组

对数组的看法,你可以创建并组装他们,通过使用整形索引值可以访问他们中的元素,并且他们的尺寸不能发生改变。

1. 数组为什么特殊

数组与其他种类的容器之间的区别有三方面:

  1. 效率,数组是效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单线性序列,使得元素访问非常快速。代价就是数组对象的大小被固定,并且在生命周期内不可改变。
  2. 类型,泛型之前,其他容器类在处理对象时,都将视作没有任何类型,也就是说,这些对象都将当作Object处理。数组之所以优于泛型之前的容器,就是可以创建一个持有具体类型的数组。
  3. 类型,泛型之前,其他容器类在处理对象时,都将视作没有任何类型,也就是说,这些对象都将当作Object处理。数组之所以优于泛型之前的容器,就是可以创建一个持有具体类型的数组。

2.数组是第一级对象

数组标识符其实只是一个引用指向在堆中创建的一个真实对象,这个数组对象用以保存指向其他对象的引用。对象数组和基本类型数组的区别主要是:对象数组保存的是引用,基本类型数组保存的是基本类型的值。

下面我们来讲解数组的几种创建方式:

import java.util.*;
import static net.mindview.util.Print.*;

public class ArrayOptions {
  public static void main(String[] args) {
    // Arrays of objects:
    BerylliumSphere[] a; // Local uninitialized variable
    BerylliumSphere[] b = new BerylliumSphere[5];
    // The references inside the array are
    // automatically initialized to null:
    print("b: " + Arrays.toString(b));
    BerylliumSphere[] c = new BerylliumSphere[4];
    for(int i = 0; i < c.length; i++)
      if(c[i] == null) // Can test for null reference
        c[i] = new BerylliumSphere();
    // Aggregate initialization:
    BerylliumSphere[] d = { new BerylliumSphere(),
      new BerylliumSphere(), new BerylliumSphere()
    };
    // Dynamic aggregate initialization:
    a = new BerylliumSphere[]{
      new BerylliumSphere(), new BerylliumSphere(),
    };
    // (Trailing comma is optional in both cases)
    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);

    // Arrays of primitives:
    int[] e; // Null reference
    int[] f = new int[5];
    // The primitives inside the array are
    // automatically initialized to zero:
    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 };
    // Compile error: variable e not initialized:
    //!print("e.length = " + e.length);
    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);
  }
}
运行结果:
b: [null, null, null, null, null]
a.length = 2
b.length = 5
c.length = 4
d.length = 3
a.length = 3
f: [0, 0, 0, 0, 0]
f.length = 5
g.length = 4
h.length = 3
e.length = 3
e.length = 2

数组创建的方式:

  1. 定义局部变量BerylliumSphere[] a;
  2. 定于固定大小的数组对象 BerylliumSphere[] b = new BerylliumSphere[5];
  3. 聚集初始化语法
 BerylliumSphere[] d = { new BerylliumSphere(),
      new BerylliumSphere(), new BerylliumSphere()
    };
  1. 动态聚集初始化语法
 a = new BerylliumSphere[]{
      new BerylliumSphere(), new BerylliumSphere(),
    };

3. 返回一个数组

在Java中,直接返回一个数组,无须担心为数组负责——只要需要它,就会一直粗壮乃,当用完了垃圾回收器会清理掉它。

Arrays.toString(a);

4.多维数组

使用花括号将每个向量分隔开:

public class MultidimensionalPrimitiveArray {
  public static void main(String[] args) {
    int[][] a = {
      { 1, 2, 3, },
      { 4, 5, 6, },
    };
    System.out.println(Arrays.deepToString(a));
  }
} /*
[[1, 2, 3], [4, 5, 6]]
*/

JavaSE5的Array.deepToString方法,可以将多维数组转换为多个String:

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

5. 数组与泛型

通常数组与泛型不能很好地结合。可以参数化数组本身的类型:

class ClassParameter<T> {
  public T[] f(T[] arg) { return arg; }
}

class MethodParameter {
  public static <T> T[] f(T[] arg) { return arg; }
}

public 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);
  }
}

参数化方法不必为需要应用的每种不同的类型都是用一个参数去实例化,并且可以将其定义为静态的。当然,不能总选择使用参数化方法而不是参数化类,但是他应该是首选。

不能创建泛型数组这一说法并不准确,编译器确实不让实例化泛型数组,但允许创建对这种数组的引用:List[] ls(可通过编译器而不报任何错误)。
尽管不能创建实际的持有泛型的数组对象,但可以创建非泛型数组然后转型:

一旦拥有了对List[]的引用,你就会看到某些编译器检查。问题是数组是协变类型的,因此List[]也是一个Object[],并且可以利用这一点,将一个ArrayList赋值到数组中,而不会有任何编译器或运行时错误。
在类或方法的内部,擦除通常会使泛型变得不适用。例如,你不能创建泛型数组:

public class ArrayOfGenericType<T> {
  T[] array; // OK
  @SuppressWarnings("unchecked")
  public ArrayOfGenericType(int size) {
    //! array = new T[size]; // 不合法的
    array = (T[])new Object[size]; // "unchecked" Warning
  }
  // Illegal:
  //! public <U> U[] makeArray() { return new U[10]; }
}

擦除再次成为了障碍——本例试图创建的类已被擦除,因而是类型未知的数组。注意,可以创建Object数组,然后将其转型,但是如果没有@SuppressWarnings注解,将在编译期得到一个不受检查的警告信息,因为这个数组没有真正持有或动态检查类型T。也就是说,如果创建一个String[],Java在编译期和运行时都会强制要求将String对象置于该数组中。但是如果创建的是Object[],那就可以将除基本类型之外的任何对象置于该数组中。

6. 创建测试数据

Arrays.fill()

fill()方法:只能用同一个值填充各个位置,而针对对象而言,就是复制同一个引用来进行填充:

7. Arrays实用功能

java.util.Arrays类:

  1. equals比较两个数组是否相等(deepEquals用于多维数组)
  2. fill 填充数组中的每一个元素
  3. sort用于数组排序
  4. binarySearch用于已经排序的数组中查询元素
  5. toString
  6. hashCode产生数组的散列码

Arrays.asList接受任意的序列或数组做为参数,应将其转化为List容器

7.1 复制数组

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

 System.arraycopy(k, 0, i, 0, k.length);

arraycopy需要参数有:源数组,表示从源数组中的什么位置开始复制的偏移量,表示从目标数组的什么位置开始复制的偏移量,以及需要复制的元素个数。
然而如果复制对象数组,那么只是复制了对象的引用——而不是对象本身的拷贝。这被称作浅复制。System.arraycopy不会执行自动包装和自动拆包,两个数组必须具有相同的确切类型。

7.2 数组的比较

重载后的equals,数组相等的条件是元素个数必须相等,并且对应位置的元素也相等,这可以通过对每一个元素使用equals作比较来判断,对于基本类型需要使用基本类型的包装器类的equals方法。

Arrays.equals(s1, s2)

7.3 数组元素的比较

程序设计的基本目的是将保持不变的事物与会发生改变的事物相分离,而这里,不变的是通用的排序算法,改变的是各种对象相互比较的方式。因此,不是将进行比较的代码编写成不同的子程序,而是使用策略设计模式。

Java有两种比较功能:

  1. java.lang.Comparable接口的comparaTo方法,此方法接受另一个Object为参数,如果当前对象小于参数则返回负值,相等返回0,大于返回正值。
  2. Arrays.sort(a, Collections.reverseOrder());

7.4 数组排序

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

Arrays.sort(sa);

String排序算法依据字典编排数序排序,所以大写字母开头的词都放在前面输出,然后才是小写。如果忽略大小写,使用CASE_INSENSITIVE_ORDER。

7.5 在已排序的数组中查找

数组已经排好序,可以使用Arrays.binarySearch执行快速查找。如果对未排序的数组使用,那么将发生不可预料的结果。

Arrays.binarySearch(a, r);

如果找到目标,Arrays.binarySeach产生的返回值等于或大于0。否则产生负值,表示如要保持数组的排序状态此目标元素所应该插入的位置。这个负值的计算方式是: - (插入点) - 1
插入点是指,第一个大于查找对象的呀un苏在数组中的位置,如果数组中所有元素都小于要查找的对象,插入点就等于a.size()。

如果数组包含重复元素,则无法保证找到的是副本中的哪一个。

如果使用Comparator排序了对象数组(基本类型数组无法使用),在使用binarySearch时必须提供同样的Comparator:

猜你喜欢

转载自blog.csdn.net/qq_21125183/article/details/85054681