Java学习笔记——数组

简介

Java和许多高级语言都提供了一种称作数组的数据结构,可以用它来存储一个元素个数固定且元素类型相同的有序集。
单个的数组变量可以引用一个大的数据集合。

数组基础知识

数组是用来存储数据的集合,通常可以把数组看作一个存储具有相同类型的变量集合。
一旦数组被创建,它的大小是固定的。使用一个数组引用变量,通过下标来访问数组中的元素。

声明数组变量

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

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

elementType可以是任意数据类型,但是数组中所有的元素都必须具有相同的数据类型。
注意: 也可以用 elementType arratRefVar[] 声明数组变量。

创建数组

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

arratRefVar = new elementType[arratSize];

声明一个数组变量、创建数组、然后将数组引用赋值给变量这三个步骤可以合并为一个步骤:

elementType[] arrayRefVar = new elementType[arraySize];
或
elementType arrayRefVar[] = new elementType[arraySize];

注意: 一个数组变量看起来似乎是存储了一个数组,但实际上它存储的是指向数组的引用。

数组的大小和默认值

当给数组分配空间时必须指定该数组能够存储的元素个数,从而确定数组大小。创建数组之后,就不能再修改它的大小。可以使用arrayRefVar.length得到数组的大小。
当创建数组后,它的元素被赋予默认值,数值型基本数据类型的默认值为0,char型的默认值为‘\u0000’,boolean型的默认值为false。

访问数组元素

数组元素可以用过下标访问。数组下标是基于0的,即其范围从0开始到arrayRefVar.length - 1结束。
数组中的每个元素都可以使用下面的语法表示,称为下标变量:

arrayRefVar[index];

注意: 一些语言使用圆括号引用数组元素,例如arrayRefVar(9)。而Java语言使用方括号,例如arrayRefVar[9]。

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

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

数组初始化语法

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

elementType[] arratRefVar = {value0, value1, value2, ..., valuek};

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

double[] myList;
myList = {1.9, 2.9, 3, 4};

处理数组

处理数组时,经常会用到for循环,理由如下:
数组中所有元素都是同一类型的。可以使用循环以同样的方式反复处理这些元素。
由于数组的大小是已知的,所以很自然地就使用for循环。

假设创建如下数组:

double[] myList = new double[10];

下面是一些处理数组的例子:

① 使用输入值初始化数组

Scanner input = new Scanner(System.in);
System.out.println("Enter " + myList.length + " value: ");
for (int i = 0; i < myList.length; i++) 
    myList[i] = input.nextDouble();

② 使用随机数初始化数组

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

③ 显示数组

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

注意: 对于char[]类型的数组,可以使用一条打印语句打印。例如下面的代码显示Dallas:

char[] city = {'D', 'a', 'l', 'l', 'a', 's'};
System.out.println(city);

④ 对所有元素求和

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

⑤ 找出最大元素

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

⑥ 找出最大元素的最小下标值

double max = myList[0];
int indexOfMax = 0;
for(int i = 1; i < myList.length; i++) {
    if(myList[i] > max) {
        max = myList[i];
        indexOfMax = i;
    }
}

⑦ 随机打乱

for(int i = myList.length - 1; i > 0; i--) {
    int j = (int)(Math.random() * (i + 1));
    double temp = myList[i];
    myList[i] = myList[j];
    myList[j] = temp;
}

⑧ 移动元素

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

⑨ 简化编码

String[] months = {"January", "February", ..., "December"};
System.out.print("Enter a month number(1 to 12): ");
int monthNumber = input.nextInt();
System.out.println("The month is " + months[monthNumber - 1]);

如果不使用 month 数组,则需要使用一个很长的多分支if-else语句来确定月份名称:

if(monthNumber == 1)
    System.out.println("The month is January");
else if(monthNumber == 2)
    System.out.println("The month is February");
...
else 
    System.out.println("The month is December");

foreach循环

Java支持一个简便的for循环,称为foreach循环,即不使用下标变量就可以顺序地遍历整个数组。例如:

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

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

通常,foreach循环的语法为:

for(elementType element : arrayRefVar) {
    //执行语句
}

注意: 当需要以其他顺序遍历数组或改变数组中的元素时,还是需要使用下标变量。

注意: 越界访问数组是经常出现的程序设计错误,它会抛出一个运行错误ArrayIndexOutOfBoundException。为了避免错误发生,使用时应确保所使用的下标不超过arrayRefVar.length - 1。
经常会出现错误地使用下标1引用数组的第一个元素,但实际上第一个元素的下标应该是0。这称为下标过1错误。它是循环中该使用 < 的地方误用 <= 时发生的错误。例如:

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

应该把 <= 替换为 < 。

实例学习:分析数字

编写一个程序,找到大于所有项平均值的那些项。

public class AnalyzeNumbers {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("Enter the number of items: ");
        int n = input.nextInt();
        double[] numbers = new double[n];
        double sum = 0;

        //输入数字
        System.out.print("Enter the number: ");
        for(int i = 0; i < n; i++) {
            numbers[i] = input.nextDouble();
            sum += numbers[i];
        }

        //计算平均值
        doubel average = sum / n;

        //记录大于平均值的数的个数
        int count = 0;
        for(int i = 0; i < n; i++) {
            if(number[i] > average) {
                count++;
            }
        }

        System.out.println("Average is " + average);
        System.out.println("Number of elements above the average is " + count);
    }
}

实例学习:一副扑克

编写一个程序,从一副扑克中随机选出4张牌

public class DeckOfCards {
    public static void main(String[] args) {
        int[] deck = new int[52];
        String[] suits = {"Spades", "Hearts", "Diamonds", "Clubs"};
        String[] ranks = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"};

        //初始化deck,为deck的元素赋值:0到51
        for(int i = 0; i < deck.length; i++)
            deck[i] = i;

        //洗牌
        for(int i = 0; i < deck.length; i++) {
            int index = (int)(Math.random() * deck.length);
            int temp = deck[i];
            deck[i] = deck[index];
            deck[index] = temp;
        }

        //显示前四张扑克
        for(int i = 0; i < 4; i++) {
            String suit = suits[deck[i] / 13];
            String rank = ranks[deck[i] % 13];
            System.out.println("Card number " + deck[i] + ": " + rank + " of " + suit);
        }
    }
}
//输出:
//Card number 6: 7 of Spades
//Card number 48: 10 of Clubs
//Card number 11: Queen of Spades
//Card number 24: Queen of Hearts

数组的复制

要将一个数组中的类容复制到另一个数组中,需要将数组的米格元素复制到另外一个数组中。

在程序中经常需要复制一个数组或数组的一部分。这种情况下可以选择使用赋值语句(=):

list2 = list1;

这条语句并不是把 list1 引用的数组内容复制给 list2, 而是将 list1 的引用赋值给 list2。这条语句之后,list1 和 list2 都指向同一个数组。

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

复制数组的三个方法:
① 使用循环语句,逐个元素进行复制操作。
② 使用 System 类中的静态方法 arraycopy。
③ 使用 clone 方法复制数组。

可以使用循环将源数组中的每个元素复制到目标数组中的对应元素。例如:

int[] sourceArray = {2, 3, 4, 5};
int[] targetArray = new int[sourceArray.length];
for(int i = 0; i < sourceArray.length; 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。

将数组传递给方法

当将一个数组传递给方法时,数组的引用被传递给方法。

正如给方法传递基本数据类型的值一样,可以给方法传递数组。例:

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

可以通过传递一个数组调用上面的方法。例如:

printArray(new int[]{3, 1, 2, 6, 4, 2});

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

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

该数组没有显式地引用变量,这样的数组被称为匿名数组。

Java使用按值传递的方式将实参传递给方法。传递基本数据类型变量的值与传递数组值有很大的不同。
1. 对于剧本数据类型参数,传递的是实参的值
2. 对于数组类型参数,参数值是数组的引用,给方法传递的是这个引用。从语义上来讲,最好的描述就是参数传递的是共享信息,即方法中的数组和传递的数组是一样的。所以,如果改变方法中的数组,将会看到方法外的数组也发生了变化。

public class Test {
    public static void main(String[] args) {
        int x = 1;
        int[] y = new int[10];

        m[x, y];

        System.out.println("x is " + x);
        System.out.println("y[0] is " + y[0]);
    }

    public static void m(int number, int[] numbers) {
        number = 1001;
        numbers[0] = 5555;
    }
}
//输出
//x is 1
//y[0] is 5555

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

从方法中返回数组

当从方法中返回一个数组时,数组的引用被返回。
可以在调用方法时向方法传递一个数组。方法也可以返回一个数组。例:

public static int[] revers(int[] list) {
    int[] result = new int[list.length];
    for(int i = 0, j = result.length - 1; i < list.length; i++, j--) {
        result[j] = list[i];
    }
    return result;
}

可变长参数列表

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

typeName... parameterName

在方法声明中,指定类型后紧跟着省略号(…)。只能给方法中指定一个可变长参数,同时该参数必须是最后一个参数。任何常规参数必须在它之前。
Java 将可变长参数作为数组对待。可以将一个数组或数目可变的参数传递给可变长参数。

public class VarArgsDemo {
    public static void main(String[] args) {
        printMax(34, 3, 3, 2, 56);
        printMax(new double[]{1, 2, 3, 4});
    }

    public static void printMax(double... numbers) {
        if(numbers.length == 0) {
            System.out.println("No argument passed");
            return;
        }

        double resule = numbers[0];
        for(int i = 1; i < numbers.length; i++)
            if(numbers[i] > result)
                result = numbers[i];

        System.out.println("The max value is " + result);
    }
}

数组查找

查找是在数组中寻找特定元素的过程,查找是计算机程序设计中经常要完成的任务。有很多用于查找的算法和数据结构,经常使用的有:线性查找和二分查找。

线性查找

线性查找法将要查找的关键字 key 与数组中的元素逐个进行比对。这个过程持续到在列表中找到与关键字匹配的元素,或者查完列表也没有找到关键字为止。

public class LinearSeach {
    public static int linearSearch(int[] list, int key) {
        for(int i = 0; i < list.length; i++) {
            if(key == list[i])
                return i;
        }
    }
}

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

二分查找法

二分查找法是另一种常见的对数值列表的查找方法。使用二分查找法的前提条件是数组中的元素必须已经排序完成。
二分查找法首先将关键字与数组的中间元素进行比较,考虑三种情况:
① 如果关键字小于中间元素,只需要在数组的前一半元素中继续查找。
② 如果关键字和中间元素相等,则匹配成功,查找结束。
③ 如果关键字大于中间元素,只需要在数组的后一半元素中继续查找关键字。
显然,二分法在每次比较之后就排除一半的数组元素。因此,对于一个有 1024(2^10) 个元素的数组,在最坏情况下,二分法只需要比较11次,而线性查找则需要1023次。

public class BinarySearch {
    public static int binarySearch(int[] list, int key) {
        int low = 0;
        int high = list.length - 1;

        while (high >= low) {
            int mid = (low + high) / 2;

        if(key < list[mid])
            high = mid - 1;
        else if(key == list[mid])
            return mid;
        else
            low = mid + 1;
        }
        return -low - 1;
    }
}

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

数组排序

如同查找一样,排序是计算机编程中非常普遍的一个任务。对于排序已经开发出很多不同的算法。

选择排序

假设要按升序排列一个数组。选择排序法先找到数列中最小的数,然后将它和第一个元素交换。接下来,在剩下的数中找到最小数,将它和第二个元素交换,直到数列剩下一个数为止。

public class SelectionSort {
    public static void selectionSort(double[] list) {
        for (int i = 0; i < list.length - 1; i++) {
            double currentMin = list[i];
            int currentMinIndex = i;

            for(int j = i + 1; j < list.length; j++) {
                if(currentMin > list[j]) {
                    currentMin = list[j];
                    currentMinIndex = j;
                }
            }

            if(currentMinIndex != i) {
                list[currentMinIndex] = list[i];
                list[i] = currentMin;
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值