JAVA基础07一维数组

7. 一维数组

7.1 数组的基础知识

一旦数组被创建它的大小是固定的使用一个数组引用变量通过下标来访问数组中的元素。

7.1.1 声明数组变置

为了在程序中使用数组必须声明一个引用数组的变量并指明数组的元索类型下面是声明数组变量的语法:

elementType[] arrayRefVar;(元素类型口数组引用变量 ;)

elementType 可以是任意数据类型但是数组中所有的元素都必须具有相同的数据类型。例如下面的代码声明变量 myList它引用一个具有 double 型元素的数组

double[] myList;

注意也可以用 elementType arrayRefVar[ ])(元素类型数组引用变量[ ]) 声明数组变量。这种来自 C/C++ 语言的风格被 Java 采纳以适用于 C/C++ 程序贞推荐使用 elementType[] arrayRefVar (元素类型 [ ]数组引用变量风格

 

7.1.2 创建数组

不同于基本数据类型变量的声明声明一个数组变量时并不在内存中给数组分配任何空间。它只是创建一个对数组的引用的存储位置如果变量不包含对数组的引用那么这个变量的值为 null除非数组已经被创建否则不能给它分配任何元素声明数组变量之后,可以使用下面的语法用 new 操作符创建数组并且将它的引用赋给一个变量

arrayRefVar = new e1ementType[arraySize]:

这条语句做了两件事情1 ) 使用 new elementType[arrayS"ize]创建了一个数组2 ) 把这个新创建的数组的引用赋值给变暈 arrayRefVar

声明一个数组变量创建数组然后将数组引用赋值给变量这三个步驟可以合并在一条语句里如下所示

elementType[] arrayRefVar = new elementType[arraySize];

(元素类型]数组引用变量 =new 元素类型[ 数组大小]; )

elementType arrayRefVar[] = new e1ementType[arraySize];

(元素类型数组引用变量 =new 元素类型[ 数组大小];)

下面是使用这条语句的一个例子

double[] myList = new double[10]:

这条语句声明了数组变量 myList, 创建一个由 10 double 型元素构成的数组并将该数组的引用赋值给 myList使用以下语法给这些元索赋值

arrayRefVar[index] = value;

例如下面的代码初始化数组:

myList[0] = 5.6;
myList[l] = 4.5;
myList[2] = 3.3;
myL1st[3] = 13.2;
myList[4] = 4.0;
myList[5] = 34.33;
myList[6] = 34.0;
myList[7] = 45.45;
myL1st[8] = 99.993;
myList[9] = 11123;

注意:一个数组变量看起来似乎是存储了一个数组,但实际上它存储的是指向数组的引用。严格地讲一个数组变量和一个数组是不同的但多教情况下它们的差别是可以忽略的。因此为了简化通常可以说 myList 是一个数组而不用更长的陈述myList 是一个含有 10 double 型元素数组的引用变量

 

7.1.3 数组大小和默认值

当给数组分配空间时必须指定该数组能够存储的元素个数从而确定数组大小创建数组之后就不能再修改它的大小。可以使用 arrayRefVar.length 得到数组的大小例如myList.length 10

当创建数组后它的元素被赋予默认值数值型基本数据类型的默认值为 0, char 型的默认值为 '\u0000'boolean 型的默认值为 false

 

7.1.4 访问数组元素

数组元素可以通过下标访问数组下标是基于 0也就是说其范围从 0 开始到 arrayRefVar.length -1结束例如,在上图的例子中数组 myList 包含 10 double 值,而且下标从 0 9

数组中的每个元素都可以使用下面的语法表示,称为下标变量 (indexed variable) :

arrayRefVar[index];(数组引用变量[下标 ];)

例如myList[9] 表示数组 myList 的最后一个元素

警告一些语言使用圆括号引用数组元素例如 myList(9)Java 语言使用方括号例如 myList[9]

创建数组后,下标变量与正常变量的使用方法相同。例如下面的代码是将 myList[0] 和 myList[l]的值相加赋给 myList[2]

myList[2] = myList[0] + myList[l];

下面的循环是将 0 赋给 myList[0], 1赋给 myList[1], ..., 9 赋给 myList[9]:

for (int i = 0;i < myList.length; i++){
myList[i] = i; 
}

 

7.1.5 数组初始化语法

Java 有一个简捷的标记称作数组初始化语法它使用下面的语法将声明数组创建数组和初始化数组结合到一条语句中:

elementType[] arrayRefVar = {valueO, valuel, valuek};
(元索类型 [] 数组引用变量={值 0,值 1,... 值 k};)

例如

double[] myList = {1.9, 2.9, 3.4, 3.5};

这条语句声明创建并初始化包含 4 个元素的数组 myList, 它等价于下列语句

double[] myList = new double[4];
myList[0] = 1.9;
myList[l] = 2.9;
myList[2] = 3.4;
myList[3] = 3.5;

警告数组初始化语法中不使用操作符 new使用数组初始化语法时必须将声明创建和初始化数组都放在一条语句中。将它们分开会产生语法错误因此下面的语句是错误的

double[] myList;
myList ={1.9, 2.9, 3.4, 3.5};

 

7.1.6 处理数组

处理数组元素时经常会用到 for 循环理由有以下两点

1 ) 数组中所有元素都是同一类型的可以使用循环以同样的方式反复处理这些元素

2 )由于数组的大小是已知的所以很自然地就使用 for 循环

 

7.1.7 foreach 循环

Java 支持一个简便的 for 循环称为 foreach 循环即不使用下标变量就可以顺序地遍历整个数组。例如下面的代码就可以显示数组 myList 的所有元素

for (double e: myList) {
System.out.println(e); 
}

此代码可以读作 myList 中每个元素 e 进行以下操作注意变量 e 必须声明为与 myList 中元素相同的数据类型

通常foreach 循环的语法为

for (elementType element: arrayRefVar) {
// Process the element
}

但是当需要以其他顺序遍历数组或改变数组中的元素时还是必须使用下标变量

警告越界访问数组是经常会出现的程序设计错误它会抛出一个运行错误 ArraylndexOut OfBoundsException为 了 避 免 错 误 的发生在使用时应确保所使用的下标不超过 arrayRefVar.length -1。

程序员经常错误地使用下标1幻用数组的第一个元素但其实第一个元素的下标应该是 0。这称为下标过 1 错误off - by - one error)它是在循环中该使用 <的地方误用 <= 时会犯的错误。例如下面的循环是错误的

for (int i = 0;i <= list.length;i++)
System.out.print(list[i] + "");

应该用 <替换 <=。

 

 

7.2 数组的复制

要将一个数组中的内容复制到另外一个中你需要将数组的每个元素复制到另外一个数组中。

在程序中经常需要复制一个数组或数组的一部分这种情况下你可能会尝试使用赋值语句(=), 如下所示

list2 = list1;

该语句并不能将 listl引用的数组内容复制给 list2, 而只是将listl的引用值复制给了 list2在这条语句之后listllist2 都指向同一个数组,如下图所示list2 原先所引用的数组不能再引用,它就变成了垃圾会被 Java 虚拟机自动收回这个过程称为垃圾回收)

Java 可以使用賦值语句复制基本数据类型的变量但不能复制数组将一个数组变量赋值给另一个数组变量,实际上是将一个数组的引用复制给另一个变量使两个变量都指向相同的内存地址。

复制数组有三种方法:

  • 1 ) 使用循环语句逐个地复制数组的元素
  • 2 ) 使用 System 类中的静态方法 arraycopy
  • 3 )使用 clone 方法复制数组

可以使用循环将源数组中的每个元素复制到目标数组中的对应元素例如下述代码使用 for 循环将 sourceArray 复制到 targetArray:

int[] sourceArray = {2 , 3, 1, 5, 10};
int[] targetArray = new int[sourceArray.length]:
for Cint i = 0: i < sourceArray.lenath: i++) {
targetArray[i] = sourceArray[i]; 
}

另一种方式是使用 java.lang.System 类的 arraycopy 方法复制数组而不是使用循环。 arraycopy 的语法如下所示

arraycopy(sourceArray, srcPos, targetArray, tarPos, length);

其中,参数 srcPos tarPos 分别表示在源数组 sourceArray 和目标数组 targetArray 中的起始位置。sourceArray 复制到 targetArray 中的元素个数由参数 length 指定例如,可以使用下面的语句改写上述循环:

System.arraycopy(sourceArray, 0, targetArray, 0,sourceArray.length);

arraycopy 方法没有给目标数组分配内存空间复制前必须创建目标数组以及分配给它的内存空间。复制完成后sourceArray targetArray 具有相同的内容但占有独立的内存空间。

注意arraycopy 方法违反了 Java 命名习惯根据命名习慣该方法应该命名为 arrayCopy(即字母 C 大写

 

 

7.3 将数组传递给方法

当将一个数组传递给方法时数组的引用被传给方法。正如前面给方法传递基本数据类型的值一样,也可以给方法传递数组例如下面的方法显示 int 型数组的元素

public static void printArray(int[] array){
    for (int i = 0;i < array.length;i++ ){
        System.out.print(array[i] + ""); 
    } 
}

可以通过传递一个数组调用上面的方法例如下面的语句调用 PrintArray 方法显示 3、1264 2:

printArrayCnew int[]{3, 1, 2, 6, 4, 2};

注意前面的语句使用下述语法创建数组

new elementType[ ]{value0, value1, … ,valuek};

该数组没有显式地引用变量这样的数组称为匿名数组anonymous array)

Java 使用按值传递pass - by - value)的方式将实参传递给方法传递基本数据类型变量的值与传递数组值有很大的不同。

对于基本数据类型参数传递的是实参的值

对于数组类型参数参数值是数组的引用给方法传递的是这个引用从语义上来讲,最好的描述就是参教传递的是共享信息(pass - by - sharing), 即方法中的数组和传递的数组是一样的。所以如果改变方法中的数组将会看到方法外的数组也变化了

例如,采用下面的代码:

public class Test {
    public static void main(String[] args){
        int x = 1;//x represents an int value
        int[] y = new int[l0];// y represents an array of int values
        m(x , y); // Invoke m with arguments x and y
        System.out.println("x is " + x);
        System.out.print!n("y[0] is " + y[0]);
    }
    public static void m(int number, int[] numbers){
        number = 1001;// Assign a new value to number
        numbers[0] = 5555;// Assign a new value to numbers[0]
    }
}

显示

x is 1 
y[0] is 5SS5

你会觉得困惑为什么在调用 m 之后 x 仍然是1, 但是 y[0]却变成了 SSSS这是因为尽管 y numbers 是两个独立的变量但它们指向同一数组,如下图所示当调用 m(x,y)时,x y 的值传递给 number numbers因为 y 包含数组的引用值所以numbers 现在包含的是指向同一数组的相同引用值。

注意数组在 Java 中是对象JVM 将对象存储在一个称作堆heap)  的内存区域中,堆用于动态内存分配

 

 

7.4 从方法中返回数组

当从方法中返回一个数组时数组的引用被返回

可以在调用方法时向方法传递一个数组方法也可以返回一个数组例如下面的方法返回一个与输人数组元素顺序相反的数组:

public static int[] reverse(int[] list) { 
    int[] result = new int[list.length]; 

    for (int i = 0, j = result.length - 1; 
         i < list.length; i++, j--) { 
        result[j]=l1st[i]; 
    }

    return result;
}

2 行创建了一个新数组 result, 4 ~7 行把数组 list的元素复制到数组 result 中。9 行返回数组例如下面的语句返回元素为 654321的新数组 list2

int[] listl = {1, 2, 3, 4, 5, 6};
int[] list2 = reverse(list1);

 

 

7.5 可变长参数列表

具有同样类型的可变长度的参数可以传递给方法并将作为数组对待。可以把类型相同但个数可变的参数传递给方法。方法中的参数声明如下

typeName… parameterName ( 类 型 名 … 参 数 名 )

在方法声明中指定类型后紧跟着省略号...)只能给方法中指定一个可变长参数同时该参数必须是最后一个参数。任何常规参数必须在它之前

Java 将可变长参数当成数组对待可以将一个数组或数目可变的参数传递给可变长参数。当用数目可变的参数调用方法时Java 会创建一个数组并把参数传给它

 

 

7.6 数组的查找

如果一个数组排好序了对于寻找數组中的一个元素二分查找比线性查找更加高效

査找searching) 是在数组中寻找特定元素的过程例如判断某一特定分数是否包括在成绩列表中。査找是计算机程序设计中经常要完成的任务有很多用于査找的算法和数据结构。本节讨论两种经常使用的方法线性查找linear searching ) 和二分查找binary searching)

 

7.6.1 线性查找法

线性査找法将要査找的关键字 key 与数组中的元素逐个进行比较这个过程持续到在列表中找到与关键字匹配的元素,或者査完列表也没有找到关键字为止如果匹配成功线性査找法返回与关键字匹配的元素在数组中的下标。如果没有匹配成功则返回 -1。下列程序清单中的linearSearch 方法给出解决方案

    LinearSearch.java
public class LinearSearch { 
/**The method for finding a key in the list */
    public static int linearSearch(int[] list, int key) { 
        for (int i = 0;i < list.length;i++){ 
            if (key = list[i]) {
                return i;
            }
        }
        return -1; 
    } 
}

为了更好地理解这个方法对下面的语句跟踪这个方法

int[] list = {1, 4, 4, 2, 5, -3, 6, 2};
int i = linearSearch(list, 4); // Returns 1 
int j- linearSearch(list, -4); // Returns -1 
int k - 1inearSearch(list, -3); // Returns 5

线性査找法把关键字和数组中的每一个元素进行比较数组中的元素可以按任意顺序排列。平均来看如果关键字存在那么在找到关键字之前这种算法必须与数组中一半的元素进行比较。由于线性査找法的执行时间随着数组元素个数的增长而线性增长所以对于大数组而言,线性査找法的效率并不高

 

7.6.2 二分查找法

二分査找法是另一种常见的对数值列表的査找方法使用二分査找法的前提条件是数组中的元素必须已经排好序。假设数组已按升序排列二分査找法首先将关键字与数组的中间元素进行比较。

考虑下面三种情况

  • 如果关键字小于中间元素,只需要在数组的前一半元素中继续査找关键字
  • 如果关键字和中间元素相等则匹配成功査找结束
  • 如果关键字大于中间元素只需要在数组的后一半元素中继续査找关键字

显然二分法在每次比较之后就排除掉一半的数组元素有时候是去掉一半的元素有时候是去掉一半加 1 个元素假设数组有个元素为方便起见假设 n 2 的幂经过第1 次比较只剩下 n/2 个元素需要进一步査找经过第 2次比较剩下n/2)/2 个元素需要进— 步査找经过 k 次比较之后需要査找的元素就剩下 n/2k 个。hl0g2n 数组中只剩 下 1 个元素就只需要再比较丨次因此在一个已经排序的数组中用二分査找法査找一个元素,即使是最坏的情况也只需要 login+l次比较对于一个有 1024 (2^10 ) 个元素的数组,在最坏情况下,二分査找法只需要比较11而在最坏的情况下线性査找要比较 1023

每次比较后数组要査找的部分就会缩小一半low high 分别表示当前査找数组的第一个下标和最后一个下标。初始条件下low 0, high list.length -1。mid 表示列表的中间元素的下标。这样mid 就是low + high)/2。下图显示怎样使用二分法从列表{2,4, 7, 10,11, 4S, S0, 59, 60, 66, 69, 70, 79}中找出关键字 11

现在知道了二分査找法是如何工作的下一个任务就是在 Java 中实现它不要急于给出一个完整的实现。逐步地实现这个程序一次一步可以从査找的第一次迭代开始,如下图a 所示它将关键字 key 和低下标 low 0髙下标 high list.length -1的列表的中间元素进行比较。如果 key<list[nrid], 就将下标 high 设置为 mid -1;如果 key = list[mid], 则匹配成功并返回 mid; 如果 key>list[mid]就将下标 low 设置为 mid+1

接下来就要考虑增加一个循环实现这个方法重复地完成査找,如上图b 所示如果找到这个关键字,或者当 low>high 时还没有找到这个关键字就结束这个査找

当没有找到这个关键字时low 就是一个插入点这个位置将插入关键字以保持列表的有序性。一种更实用的方法是返回插入点减去1这个方法必须返回一个负值表明这个关键字不在该序列中。可以只返回 - low 吗答案是不可以如果关键字小于 list[0], 那么low 就是 0, -0 也是 0这就表明关键字匹配 list[0]一个好的选择是如果关键字不在该序列中,方法返回 - low -1。返回不仅表明关键字不在序列中而 且 还 给出了关键字应该插人的地方。

下面的表格列出当方法退出时 low high 的值以及调用该方法的返回值

注意线性查找法适用于在较小数组或没有排序的数组中查找但是对大数组而言效率不高。二分查找法的效率较高但它要求数组已经排好序

 

 

7.7 数组的排序

假设要按升序排列一个数列选择排序法先找到数列中最小的数然后将它和第一个元素交换。接下来在剩下的数中找到最小数将它和第二个元素交换依此类推,直到数列中仅剩一个数为止。下图显示如何使用选择排序法对数列{2,9,5,4,8,1,6}进行排序

已经知道了选择排序法是如何工作的现在的任务是用 Java 语言实现它对初学者来说,很难在第一次尝试时就开发出完整的解决方案开始编写第一次迭代的代码找出数列中的最大数,将其与最后一个元素互换然后观察第二次迭代与第一次的不同之处接着是第三次,依此类推通过这样的观察可以写出推广到所有迭代的循环

for (int i = 0;i < list. length - 1; i++){
    select the smallest element in list[i...list.length-1];
    swap the smallest with list[i],if necessary; 
    // list[i] is in its correct position. 
    // The next iteration applies on list[i+l..list.length-1]
}

 

 

7.8 Arrays

java.util.Arrays 类包含一些实用的方法用于常见的數组操作比如排序和查找。

java.util.Arrays 类包括各种各样的静态方法用于实现数组的排序和査找数组的比较和填充数组元素,以及返回数组的字符串表示这些方法都有对所有基本类型的重载方法

可以使用 sort 或者 parallelSort方法对整个数组或部分数组进行排序例如下面的代码对数值型数组和字符型数组进行排序。

double[] numbers = {6.0, 4.4, 1.9, 2.9, 3.4, 3.5};
java.util .Arrays.sort(numbers);// Sort the whole array
java.util .Arrays.parallelSort(numbers): // Sort the whole array
char[] chars = {'a', 'A', '4', 'F', 'D', 'P'};
java.util .Arrays.sort(chars, 1,3);// Sort part of the array
java.util .Arrays.parallelSort(chars, 1, 3); // Sort part of the array

可以调用sort(numbers)对整个数组 numbers 排序可以调用sort(chars,1, 3)对从 chars[1]chars[3-1]的部分数组排序如果你的计算机有多个处理器那么 parallelSort 将更加高效。

可以采用二分査找法binarySearch 方法在数组中査找关键字数组必须提前按升序排列好。如果数组中不存在关键字方法返回 -( 插入点下标 +1)例如下面的代码在整数数组和字符数组中査找关键字:

int[] list = {2 , 4, 7, 10, 11, 45 , 50 , 59, 60, 66 , 69, 70, 79 } ;
System.out.println("l. Index is " +
java.util .Arrays.binarySearch(list, 11));
System.out.println("2. Index is " +
java.util .Arrays.binarySearch(list, 12));
char[] chars = {'a' , 'c' , 'g' , 'x' , 'y' , 'z' };
System.out.println("3. Index is " +
java.util .Arrays.binarySearch(chars, 'a'));
System.out.println("4. Index is " +
java.util .Arrays.binarySearch(chars, 't' ));

前面代码的输出为

1. Index is 4 
2. Index is — 6 
3. Index is 0 
4. Index is — 4

可以采用 equals 方法检测两个数组是否相等如果它们的内容相同那么这两个数组相等。在下面的代码中listl list2 相等list2 list3 不相等

int[] listl = {2 , 4 , 7,  10};
int[] list2 = {2 , 4 , 7 , 10};
int[] Tist3 = {4 , 2 , 7 , 10};
System.out. println(java.uti1.Arrays.equals(listl, list2)); // true
System.out. println(java.uti1.Arrays.equals(list2, list3)); // false

可以使用 fill 方法填充整个数组或部分数组例如下列代码将 5 填充到 list1 ,将 8 填充到元素 list2[1] list2 [5-1]

int[] listl = {2 , 4, 7, 10};
int[] list2 = {2 , 4 , 7, 7, 7, 10};
java.util .Arrays.fiil (list1, 5); // Fill 5 to the whole array
java.util .Arrays.fiil (list2, 1, 5, 8); // Fill 8 to a partial array

还可以使用 toString 方法来返回一个字符串该字符串代表了数组中的所有元素这是一个显示数组中所有元素的快捷和简便的方法。例如下面代码

int[] list = {2, 4, 7, 10};

System.out.println(Arrays.toString(list));

显示 [ 2 , 4 , 7, 10]。

 

 

 

 

编译运算练习

1.数组遍历

 

 

2.数组线性查找

 

 

3.数组二分查找

 

 

4.数组扩容

 

 

5.数组选择排序

 

 

6.数组冒泡排序

 

 

7.数组插入(希尔)排序

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值