本帖最后由 ufof 于 2016-5-5 00:32 编辑



7.1 定义以及初始化数组



7.1.1 认识数组

在编程当中,很多时候我们需要使用到许多同类型的数据。如果数量较多的话,程序会显得较为笨重。例如:

  1. class ArrayDemo {
  2.     public static void main(String[] args) {
  3.         int num1 = 3;
  4.         int num2 = 2;
  5.         int num3 = 7;
  6.         int num4 = 5;
  7.         int num5 = 4;        
  8.     }
  9. }
复制代码

在这个例子当中,有大量的int类型数据重复。程序显得没有弹性,臃肿。数组就是用来解决这个问题。数组是一个可以储存若干个同类型数据的类型

在Java中,数组并非一个类,而是一个特殊的引用类型数据。现在我们来学习如何定义一个数组。

7.1.2 定义数组

定义数组的语法如下:
  1. 数组类型[] 数组名;
复制代码
或:
  1. 数组类型 数组名 [];
复制代码
当定义一个数组时,数组所在的方法的栈区会新建一个数组的引用。
我们现在来定义一个可以存储int类型的数组。

  1. int[] arr;
复制代码

好的,这个部分很简单。但是我们现在的数组还没有值,我们需要初始化这个数组。请看下一节。

7.1.3 动态初始化

动态初始化是只指定数组的长度,而不对其的元素进行设置。元素的值会是其对应的类型的默认值。

语法如下:

  1. 数组类型 数组名 = new 数组类型[数组长度];
复制代码
这个初始化的方法的弊端就在于元素需要我们一个一个去赋值。那么这又引出来了一个新的问题,如何访问数组中的单个元素?

在Java中,通过以下语法访问数组中的元素:

  1. 数组名[下标]
复制代码

下标从0开始。

好的,现在我们来定义一个int类型数组,对其的值进行赋值。

  1. class ArrayDemo {
  2.     public static void main(String[] args) {
  3.         int arr[] = new int[5];     //定义并动态初始化int类型数组,将其长度设置为5
  4.         //访问数组元素,并赋值
  5.         arr[0] = 2;
  6.         arr[1] = 4;
  7.         arr[2] = 1;
  8.         arr[3] = 7;
  9.         arr[4] = 6;
  10.         
  11.         //逐个打印数组元素
  12.         System.out.println(arr[0]+","+arr[1]+","+arr[2]+","+arr[3]+","+arr[4]);
  13.     }
  14. }
复制代码
结果:



在这个例子当中,我们可以发现,动态初始化的方式需要我们手动的对数组中的每一个元素进行赋值。动态初始化的数组的元素全部为其对应的类型的默认值,例如int类型的默认值是0,int类型的数组动态初始化后其中所有的元素都为0。


那么,有没有一个初始化方法,在初始化的时候就已经把元素的值确定下来的呢?

7.1.4 静态初始化

静态初始化可以在定义数组时就将其的元素全部赋值好。语法如下:

  1. 数组类型 数组名 = new 数组类型[]{元素值1,元素值2,元素值3,.....元素值n};
复制代码
可以被简写成:

  1. 数组类型 数组名 = {元素值1,元素值2,元素值3,.....元素值n};
复制代码
我们用这个方法来解决我们刚才的问题:

  1. class ArrayDemo {
  2.     public static void main(String[] args) {
  3.         int[] arr = {2,4,1,7,6};    //静态初始化
  4.         System.out.println(arr[0]+","+arr[1]+","+arr[2]+","+arr[3]+","+arr[4]);    //打印元素
  5.     }
  6. }
复制代码
结果:



这种方式比上一中的简便多了。但是如果在编程的时候的确在定义数组时其值不确定,可以动态初始化。

数组还有一个特点,一经初始化,长度无法改变。
学生提问:数组可以存储引用类型数据吗?

答:可以。例如我一个数组也可以用来存String类的数据。例如:
  1. String[] arr = {"abc","def"};
复制代码

本章小结
  • 数组是一个储存若干个同类型数据的类型
  • 数组的定义方式为“数组类型 数组名”
  • 动态初始化语法为“数组类型 数组名 = new [长度]”
  • 静态初始化语法为“数组类型 数组名 = {元素值1,元素值2,元素值3,.....元素值n}”
  • 访问数组元素的方式为“数组名[下标]”



7.2 遍历数组以及foreach



7.2.1 遍历数组概念

遍历数组可以理解获取数组中的每一个元素,一般是打印、判断等。我们可以通过传统的for循环,以及较新的foreach方式来遍历。

7.2.2  数组的length字段

在通过for遍历数组时,需要获取数组的长度来提供循环条件。所以在学遍历之前先学一下length字段。

length很容易理解,就是数组的长度。

  1. class ArrayDemo {
  2.     public static void main(String[] args) {
  3.         int[] arr = {2,4,1,7,6};
  4.         System.out.println(arr.length);
  5.     }
  6. }
复制代码
结果:



在这个例子中,arr一共有五个元素。打印length字段则为5。相信很容易理解。

7.2.3 for遍历

for遍历一般先定义一个整数类型,判断条件是让这个整数类型与数组长度进行比较。实例如下:

  1. class ArrayDemo {
  2.     public static void main(String[] args) {
  3.         int[] arr = {2,4,1,7,6};
  4.         for(int x = 0;x<arr.length;x++){
  5.             System.out.print(arr[x]+",");
  6.         }
  7.     }
  8. }
复制代码
结果:



for循环中定义了变量x,循环条件是其是否小于数组的长度。循环体中打印arr的x下标元素。为了不让其换行,使用了print()方法而不是println()方法。

不过有一个小问题,打印最后一个元素时也有一个逗号。我们可以判断x是否为最后一个元素,然后进行输出。

  1. class ArrayDemo {
  2.     public static void main(String[] args) {
  3.         int[] arr = {2,4,1,7,6};
  4.         for(int x = 0;x<arr.length;x++){
  5.             if(arr.length-1==x){
  6.                 System.out.print(arr[x]);
  7.             }
  8.             else{
  9.                 System.out.print(arr[x]+",");
  10.             }
  11.         }
  12.     }
  13. }
复制代码
结果:




每次循环中判断x是否等于length-(至于为什么-1是因为下标是从0开始的),然后进行对应的打印输出。

7.2.4 foreach

foreach是一种更加简便的遍历方式,但是其不是一个关键字,在使用时用到的关键字仍然是for。语法如下:

  1. for(整数类型 变量名:数组){
  2.     //遍历语句
  3. }
复制代码
其中定义的变量是用于依序遍历数组的。每循环一次+1。(感谢@cesium_floride的提醒)
引用我们的例子,我们可以这样写:

  1. class ArrayDemo {
  2.     public static void main(String[] args) {
  3.         int[] arr = {2,4,1,7,6};
  4.         for(int x:arr){
  5.             System.out.println(x);
  6.         }
  7.     }
  8. }
复制代码
结果:


这种写法更加方便,比较推荐大家使用。

本章小结
  • 遍历是指获取数组中的每一个元素
  • for可以用来遍历
  • foreach是一个更加简便的遍历方法



7.3 获取最值算法



7.3.1 算法概述

Java并没有为我们提供数组最值的方法。所以说如果要获取数组中的最大值/最小值,需要我们自己去写一个小工具。这一节我们就来介绍一下算法。

算法其实很简单,就是遍历数组中的每一个元素,用一个变量来记住元素的值。如果第n个元素比第n-1个元素大,用这个变量记住第n个元素即可。最小值同理。

7.3.2  实现算法

既然要遍历,肯定要用到for。而且需要定义一个变量来记住最值(这里先示范最大值),将这个变量初始化为数组的第一个元素。
  1. public static int getMax(int[] arr){
  2.     int max = arr[0];
  3.     for(int element = 1;element<arr.length;element++){
  4.             
  5.     }
  6. }
复制代码
现在,我们来判断arr的element下标元素是否大于max:

  1. public static int getMax(int[] arr){
  2.     int max = arr[0];
  3.     for(int element = 1;element<arr.length;element++){
  4.         if(arr[element]>max){
  5.             max = arr[element];
  6.         }
  7.     }
  8. }
复制代码
学生提问:为什么不用foreach?

答:因为在foreach中定义的变量的初始值肯定为0,不能是1。然而第0个下标元素和自己比较是没有意义的。如果要用foreach也可以,只不过多了一个不需要的比较罢了。

最后,我们再把max返回即可。

  1. public static int getMax(int[] arr){
  2.     int max = arr[0];
  3.     for(int element = 1;element<arr.length;element++){
  4.         if(arr[element]>max){
  5.             max = arr[element];
  6.         }
  7.     }
  8.     return max;
  9. }
复制代码
我们在主方法中调用getMax()方法,测试一下:

  1. class GetMaxDemo {
  2.     public static void main(String[] args) {
  3.         int[] arr = {4,8,6,1,2,5};
  4.         System.out.println(getMax(arr));
  5.     }
  6.    
  7.     public static int getMax(int[] arr){
  8.         int max = arr[0];
  9.         for(int element = 1;element<arr.length;element++){
  10.             if(arr[element]>max){
  11.                 max = arr[element];
  12.             }
  13.         }
  14.         return max;
  15.     }
  16. }
复制代码
结果:



如果要判断最小值,把if中的判断语句改成小于即可。

本章小结
  • 通过对元素的遍历记住最大值并返回即可
  • 也可以用foreach,只是多了一次无意义的比较而已




7.4 Arrays类



7.4.1 binarySearch()方法

Arrays类是Java为我们提供的数组工具类,其中的许多方法都十分常用,而且都为static,所以可以直接被类调用。
binarySearch()通过折半查找算法,在数组中查找指定的值,并返回角标。其有多个方法被重载。其接收两个参数:第一个参数是一个数组引用,不同的方法接收不同类型的数组;第二个参数是要查找的元素的值。

不过要注意:由于Arrays类在java.util包下,需要导包。这点我们在面向对象(下)中会学到。在这里大家就先把这行代码加在程序的第一行:

  1. import java.util.Arrays;
复制代码

上整个实例的代码:

  1. import java.util.Arrays;

  2. class ArraysDemo {
  3.     public static void main(String[] args) {
  4.         int[] arr = {2,5,3,7,5,4};
  5.         System.out.println("3在arr数组中的角标为"+Arrays.binarySearch(arr, 3));
  6.     }
  7. }
复制代码
结果:



3在数组中为第三个元素,对应的下标就是2。通过binarySearch()方法,传入arr数组以及要查找的值3,其将返回其所在的角标。
如果没有找到这个值,返回一个负数。

7.4.2 equals()方法

equals()方法接收两个同类型的数组,返回值为boolean类型。如果两个数组的长度一样,而且元素值也一样,返回true;反之亦然。

  1. import java.util.Arrays;

  2. class ArraysDemo {
  3.     public static void main(String[] args) {
  4.         int[] arr = {2,5,3,7,5,4};
  5.         int[] arr2 = {2,5,3,7,5,4};
  6.         System.out.println("arr和arr2是否相等?"+Arrays.equals(arr, arr2));
  7.     }
  8. }
复制代码
结果:



  1. import java.util.Arrays;

  2. class ArraysDemo {
  3.     public static void main(String[] args) {
  4.         int[] arr = {2,5,3,7,5,4};
  5.         int[] arr2 = {2,5,3,7,5,4,1};
  6.         System.out.println("arr和arr2是否相等?"+Arrays.equals(arr, arr2));
  7.     }
  8. }
复制代码
结果:




7.4.3 sort()方法

sort()方法无返回值,接收数值类型的数组,以及引用类型数组。对于数值类型数组,使用sort()方法会对其进行排序(从小到大);sort()方法也可以接收引用类型数组,但是我们先不讲。


  1. import java.util.Arrays;

  2. class ArraysDemo {
  3.     public static void main(String[] args) {
  4.         int[] arr = {43,23,36,64,15};
  5.         Arrays.sort(arr);    //排序数组
  6.         for(int x = 0;x<arr.length;x++){    //遍历排序之后的数组
  7.             if(x==(arr.length-1)){
  8.                 System.out.print(arr[x]);
  9.             }
  10.             else{
  11.                 System.out.print(arr[x]+",");
  12.             }
  13.         }
  14.     }
  15. }
复制代码
结果:



arr数组原本是无序的一个int类型数组。通过sort()方法排序之后,在进行遍历打印输出,可以发现其中的元素被从小到大排序了。

7.4.4 toString()方法

toString()方法用于将数组转换成一个字符串的表达形式。
  1. import java.util.Arrays;

  2. class ArraysDemo {
  3.     public static void main(String[] args) {
  4.         int[] arr = {43,23,36,64,15};
  5.         System.out.println(Arrays.toString(arr));
  6.     }
  7. }
复制代码
结果:



这个方法十分方便。以后都不需要遍历数组来打印输出了,直接转换成字符串打印输出即可。

本章小结
  • binarySearch()通过折半查找算法返回指定的元素值的下标
  • equals()方法返回布尔,用于比较两个同类型数组元素知否完全一样
  • sort()方法用于排序数组
  • toString()用于将数组转换成字符串表达形式



7.5 两种排序算法



7.5.1 选择排序

虽说Arrays类已经给我们提供了sort()方法对数组进行排序,但是为了让我们对算法更多地了解,我们还是来手写几个排序的算法。
选择排序是一个很简单的排序算法。这种算法很容易理解,但是不太稳定。我们先从这个算法入手吧。

在排序的第一轮
:[0]要和[1]相比较,如果[0]>[1],两者交换位置;然后[0]要和[2]比较,如果[0]>[2],交换。以此类推,直到比较到最后一个元素。
在排序的第二轮:[1]要和[2]相比较,然后比较[1]和[3]...以此类推
在排序的第三轮:[2]要和[3]相比较,
然后比较[2]和[4]...以此类推。
以此类推...
大家可以发现一个规律:在第n轮中,第n-1个角标不需要参与运算。

一共需要比较n-1次(n为数组长度)。现在我们用代码来实现吧!

首先我们知道要比较n-1次,所以我们来写一个for循环:
  1. public static int[] selectionSort(int[] arr){
  2.         for(int x = 0;x<arr.length;x++){
  3.             
  4.         }
  5. }
复制代码
之所以要arr.length-1是因为最后一个角标和自己比没有任何意义。

在每一轮中,我们需要让相邻的两个角标比较。由于“
在第n轮中,第n-1个角标不需要参与运算”,我们定义内部for的变量时可以初始化为x+1。

  1. public static int[] selectionSort(int[] arr){
  2.     for(int x = 0;x<arr.length;x++){
  3.         for(int y = x+1;y<arr.length;y++){
  4.             if(arr[y]<arr[y-1]){
  5.                     
  6.             }
  7.         }
  8.     }
  9. }
复制代码

在内部的for循环中,如果[y]<[x],那么就要交换两个值。那么如何交换呢?

首先我们需要第三个值来记住[y],然后把arr[x]赋给[y],最后把temp赋给[x]。
  1. public static int[] selectionSort(int[] arr){
  2.     for(int x = 0;x<arr.length;x++){
  3.         for(int y = x+1;y<arr.length;y++){
  4.             if(arr[y]<arr[x]){
  5.                     int temp = arr[y];
  6.                     arr[y] = arr[x];
  7.                     arr[x] = temp;
  8.             }
  9.         }
  10.     }
  11. }
复制代码
这样的写法是可以的。但是有一个不好的地方:我在循环中声明一个temp变量。由于是在for中,程序会一直声明一个变量,这样的做法很耗费栈内存。所以说我们可以把temp在循环外面先声明,但是不初始化:


而且最后把arr返回即可。
  1.     public static int[] selectionSort(int[] arr){
  2.         int temp;
  3.         for(int x = 0;x<arr.length-1;x++){
  4.             for(int y = x+1;y<arr.length;y++){
  5.                 if(arr[y]<arr[x]){
  6.                     temp = arr[y];
  7.                     arr[y] = arr[x];
  8.                     arr[x] =temp;
  9.                 }
  10.             }
  11.         }
  12.         return arr;
  13.     }
复制代码

ok,现在这个方法写完了,我们在主方法中测试一下。
  1. import java.util.Arrays;

  2. public class ArraySort {
  3.     public static void main(String[] args) {
  4.         int[] arr = {3,5,4,2,5,7};
  5.         int[] newArr = selectionSort(arr);
  6.         System.out.println(Arrays.toString(newArr));
  7.     }
  8.     public static int[] selectionSort(int[] arr){
  9.         for(int x = 0;x<arr.length;x++){
  10.             for(int y = x+1;y<arr.length;y++){
  11.                 if(arr[y]<arr[x]){
  12.                     int temp = arr[y];
  13.                     arr[y] = arr[x];
  14.                     arr[x] =temp;
  15.                 }
  16.             }
  17.         }
  18.         return arr;
  19.     }
  20. }
复制代码
结果:



可以发现,[3,5,4,2,5,7]数组被排序成了[2,3,4,5,5,7]。这个算法就写完了。

7.5.2 冒泡排序

冒泡排序同样是一个很容易理解的排序算法。

第一轮:[0]和[1]比较,如果[0]>[1]替换,然后比较[1]和[2],比较[2]和[3]...[n-1]和[n]
第二轮:从[0]和[1]
比较到[n-2]和[n-1]
第三轮:从[0]和[1]比较到[n-3]和[n-2]
第m论:从[0]到[1]比较到[n-m]和[n-m+1]

这个算法最大的特点就是每一轮较大的值都会满满地“浮”到最后,因此命名冒泡排序。
最坏的情况也是一共需要比较n-1轮。

通过代码实现:
首先定义一个for循环,这个选择排序一模一样:
  1. public static int[] bubbleSort(int[] arr){
  2.     for(int x = 0;x<arr.length;x++){
  3.             
  4.     }
  5. }
复制代码
ok,现在我们再定义一个内部for,用于比较相邻的两个元素:

  1. public static int[] bubbleSort(int[] arr){
  2.     for(int x = 0;x<arr.length;x++){
  3.         for(int y = 0;y<arr.length-x-1;y++){
  4.             
  5.         }
  6.     }
  7. }
复制代码
为什么要arr.length-x-1呢?请大家在看一下上面对算法的解释:在第二轮中,[n]角标不用比较了,在第三轮中[n-1]不用比较了。所以说y减掉x(x就是轮数)可以讲不需要比较的元素省去。

那么为什么要-1呢?假设内部for已经是最后一次循环了,也就是说y=arr.length-x,如果要比较[y]和[y+1],[y+1]就角标越界了。为了防止越界,对其-1即可。

接下来就是比较和交换了:

  1. public static int[] bubbleSort(int[] arr){
  2.     int temp;
  3.     for(int x = 0;x<arr.length;x++){
  4.         for(int y = 0;y<arr.length-x-1;y++){
  5.             temp = arr[y];
  6.             arr[y] = arr[y+1];
  7.             arr[y+1] = temp;
  8.         }
  9.     }
  10.     return arr;
  11. }
复制代码
我们在主方法中测试一下:

  1. import java.util.Arrays;

  2. public class ArraySort {
  3.     public static void main(String[] args) {
  4.         int[] arr = {6,5,4,3,2,1};
  5.         int[] newArr = bubbleSort(arr);
  6.         System.out.println(Arrays.toString(newArr));
  7.     }
  8.         
  9.     public static int[] bubbleSort(int[] arr){
  10.         int temp;
  11.         for(int x = 0;x<arr.length;x++){
  12.             for(int y = 0;y<arr.length-x-1;y++){
  13.                 temp = arr[y];
  14.                 arr[y] = arr[y+1];
  15.                 arr[y+1] = temp;
  16.             }
  17.         }
  18.         return arr;
  19.     }
  20. }
复制代码
结果:



本章小结:
  • 数组中有两个常用的排序算法:选择排序和冒泡排序
  • 在交换两个值的时候,要使用第三个值对其中的一个值进行缓存
  • 不推荐在循环当中定义类型

我本来还想讲希尔排序的结果发现新手理解不了



7.6  二维数组



7.6.1  二维数组的定义

数组可以用来储存同一个类型的若干个数据。但是现在我们遇到的问题就是里面存的数据还是一个数组,可以理解为“一个数组包含多个数组”。

二维数组的定义方式如下:

  1. 数据类型[][] 数组名;
复制代码

例如:
  1. int[][] array;
复制代码

7.6.2 二维数组的动态和静态初始化

其实二维数组的两种初始化方法和一维数组也差不多。
动态初始化语法:

  1. 数据类型[][] 数组名 = new int[行][列];
复制代码
静态初始化语法:
  1. 数组类型[][] 数组名 = {第一个数组,第二个数组,第三个数组....,第四个数组};
复制代码

访问数组中的元素:

  1. 数组名[行][列]
复制代码
内存中的二维数组:元素指向一维数组


7.6.3 二维数组遍历


对于二维数组的遍历,需要使用到双层嵌套for:

  1.         int[][] array = {{2,3},{4,2},{5,6},{3,7}};
  2.         
  3.         for(int x = 0;x<array.length;x++){
  4.             for(int y = 0; y<array[x].length;y++){
  5.                 if(x==array.length-1 && y==array[x].length-1){
  6.                     System.out.print(array[x][y]);
  7.                 }
  8.                 else{
  9.                     System.out.print(array[x][y]+",");
  10.                 }
  11.             }
  12.         }
复制代码
结果:



在外部的for当中,判断条件是x是否小于array.length。这个不难理解。
但是内部循环中,判断条件是y是否小于array的第x个角标元素的length。由于二维数组中的元素还是数组,所以可以对其的元素调用length方法。

内部的if else语句也需要说明一下。首先,如果已经遍历到了最后一个元素,而且是最后一个元素的最后一个元素,才可以输出的时候不带逗号,否则需要逗号。

本章小结
  • 二维数组的元素是数组
  • 二维数组的定义和初始化和一维数组差不多
  • 遍历二维数组需要使用嵌套for循环

[groupid=546]Command Block Logic[/groupid]