数组是指一组数据的集合,数组中的每个数据称为元素。在 Java 中,数组也是 Java 对象。数组中的元素可以是任意类型(包括基本类型和引用类型),但同一个数组里只能存放类型相同的元素。
14.1 数组变量的声明与创建
声明
int[] scores; //int scores[];
String[] names; //String names[]; 同样合法
int[][] matrix; //int x[][]; int[]x[];
创建
int[] scores = new int[100];
数组对象和其他 Java 对象一样,也用 new 语句创建。new
语句执行以下步骤:
- 堆区中为数组分配内存空间。
- 为数组中的每个元素赋予其数据类型的默认值。
- 返回数组对象的引用。
对于Java 类的程序入口方法 main(String[] args)
,如果运行这个类时没有输入参数,那么 main() 方法的参数 args 并不是 null, 而是一个长度为 0 的数组。
数组对象创建后,它的长度是固定的。数组对象的长度是无法改变的,但是数组变量可以改变所引用的数组对象。
int[] x = new int[1];
int[] y = x;
x = new int[4];
数组被创建后,每个元素被自动赋予其数据类型的默认值,还可以在程序中将数组元素显式地初始化。
int[] a = {4, 5, 6};
int[] b = new int[] {4, 5, 6};
int[] x= new int[5] {5, 4, 3, 2, 1};//编译出错,不能在[]中指定数组的长度
int[]x; x = {5, 4, 3, 2, 1}; //编译出错,{5,4,3..}必须在声明数组变量语句中使用,不能单独使用
14.2 访问数组的元素和长度
数组中的每个元素都有一个索引,或者称为下标。数组中的第一个元素的索引为0。
所有 Java 数组都有一个长度属性,它的声明形式为:
public final length;
当数组的元素为引用类型,数组中存放的是对象的引用,而不是对象本身。
StringBuffer sb = new StringBuffer("a"); //创建一个StringBuffer对象
StringBuffer[] sbs = new StringBuffer[]{sb, null}; //把他的引用加入到一个StringBuffer数组中
//此时 StringBuffer 对象 被变量sb引用,且也被 StringBuffer 数组中的第一个元素引用
sb.append("b");
sb = null;
System.out.println(sb); //打印null
sbs[0].append("c");
System.out.println(sbs[0]); //打印abc
sbs[0] = null; //StringBuffer对象结束生命周期
sbs = null; //StringBuffer数组对象结束生命周期
//第三行和第六行都向同一个StringBuffer对象添加字符
//第八行执行完,StringBuffer对象不被任何引用变量引用
14.3 多维数组以及不规则数组
Java 支持多维数组,并且支持不规则数组,即多维数组中两维以上的子数组的长度不一样。
int[][] matrix = {
{1},
{1, 2, 3},
{5, 6, 7, 8}
}
创建多维数组时,必须按照从低纬度到高纬度的顺序创建每一维数组。
int[][][] a = new int[2][][3]; //编译出错,必须先创建第二维数组再创建第三维数组
14.4 调用数组对象的方法
Java 数组继承了 Object
类,因此继承了Object类的所有方法。比如 equals()
,还可以用 instanceof
操作符。
int[] a = new int[4];
int[] b = new int[4];
boolean b1 = a instanceof Object;
boolean b2 = a instanceof int[];
boolean b3 = a.equals(b); //false
14.10 哈希表
在一般的数组中,元素的取值和元素的位置之间不存在确定的关系,因此,在数组中查找特定值时,需要把查找值和 一系列的元素进行比较。如果元素的值 value 和它在数组中的索引位置 index 有一个确定的对应关系 hash()
,那么对于给定的值,只要调用哈希方法,就能找到数组中取值为 value 的元素的位置。如果数组中元素的值和位置存在确定的对应关系,这样的数组称为 哈希表,这种数组的优点是能够提高查找数据的效率。
hash() 方法的返回值也称为元素的哈希码,但在哈希码很大或存在哈希冲突情况下,不能把哈希码直接作为元素的位置。
//MyHashSet.java
class Node {
private Object value;
private Node next;
public Node(Object vlaue) { this.value = value; }
public Object getValue() { return value; }
public Node getNext() { return next; }
public void setNext(Node next) { this.next = next; }
}
public classMyHashSet {
private Node[] array;
private int size = 0;
public MyHashSet(int length) { array = new Node[length]; }
public int size() {return size;}
private static int hash(Object o) { //获得对象的改善的哈希码
int h = o.hashCode();
h += ~ (h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
private int indexFor(int hashCode) { //通过对象哈希码获得其索引位置
return hashCode & (array.length - 1);
}
public void add(Object value) {
int index = indexFor(hash(value));
System.out.println("index: " + index + "value: " + value);
Node newNode = new Node(value);
Node node = array[index];
if (node == null) {
array[index] = new Node;
size++;
} else { //哈希冲突
Node nextNode;
while (!node.getValue().equals(value)
&& (nextNode = node.getNext())!= null) {
node = nextNode;
}
if (!node.getValue().equals(value)) {
node.setNext(newNode);
size++;
}
}
}
public boolean contains(Object value) { //测试集合中是否存在指定对象
int index = indexFor(hash(value));
Node node = array[index];
while (node != null && !node.getValue().equals(value)) {
node = node.getNext();
}
if (node != null && node.getValue().equals(value))
return true;
else
return false;
}
public boolean remove(Object value) { //删除集合中的一个对象
int index = indexFor(hash(value));
Node node = array[index];
if (node != null && node.getValue().equals(value)) {
array[index] = node.getNext();
size--;
return true;
}
Node lastNode = null;
while (node != null && !node.getValue().equals(value)) {
lastNode = node;
node = node.getNext();
}
if (node != null && node.getValue().equals(value)) {
lastNode.setNext(node.getNext());
size--;
return true;
} else {
reutrn false;
}
}
public Object[] getAll() {
Object[] values = new Object[size];
int index = 0;
for (Node node : array) {
while (node != null) {
values[index++] = node.getValue();
node = node.getNext();
}
}
return values;
}
public static void main(String[] args) {
MyHashSet set = new MyHashSet(6);
Object[] values = {"Tom", "Mike","Mike","Jack", "Mary", "Linda","Rose","Jone"};
for (Object value : values) set.add(value);
set.remove("Mary");
values = set.getAll();
...
}
}
//array[0] -> Node(Linda) -> Node(Rose) -> Node(Jone)
//array[1] -> Node(Mike)
//array[2] null
//array[3] null
//array[4] -> Node(Tom)
//array[5] -> Node(Jack) -> Node(Mary)
14.11 数组实用类:Array
equals()
:比较两个数组是否相同。只有当两个数组中的元素数目相同,并且对应位置的元素都相同时,才表明数组相同。fill()
:向数组中填充数据sort()
:把数组中的元素按升序排列。如果数组中的元素为引用类型,会采用自然排序方式。parallelSort()
:开启多个线程,以并发的方式对数组中的元素进行排序,提高排序的效率。binarySearch()
:按照二分查找算法,查找数组中值与给定数据相同的元素。在调用该方法时,必须保证数组中的元素已经按照升序排列,这样才能得到正确的结果。asList()
:把数组转换成一 个 List 对象,将其返回。toString()
:返回包含数组中所有元素信息的字符串。
以上每个方法都有多种重载形式
14.12 声明数目可变参数
为了进一步简化编程,JDK5增加了一个新特性:用符号...
来声明数目可变参数(简称为可变参数)。可变参数适用于参数的数目不确定,而类型确定的情况。
void method(int a, float b, String... c)
可变参数具有以下特点:
- 只能出现在参数列表的最后,作为最后一个参数。
- 符号
...
位于参数类型和参数名之间,前后有无空格都可以。 - Java 虚拟机在运行时为可变参数隐含创建一个数组,因此在方法体内允许以数组的形式访问可变参数。