07-一维数组
-
7.1 引言
- 单个的数组变量可以引用一个大的数据集合。
-
7.2 数组的基础知识
一旦数组被创建,他的大小是固定的。使用一个数组引用变量和下标来访问数组中的元素。
- 7.2.1 声明数组变量
- 为了在程序中使用数组,必须声明一个引用数组的变量,并指明数组的元素类型。下卖弄是声明数组变量的语法:
elementType[] arrayRefVar; //或者 elementType arrayRefVar[];//是允许这样的,但是不是最好的方式
- elementType可以是任意数据类型,但是数组中所有的元素都必须具有相同的数据类型。
- 也可以用elementType arrayRefVar[]声明数组变量。这种来自c/c++语言的风格被Java采纳以适用于c/c++程序员。推荐使用elementType[] arrayRefVar。
- 为了在程序中使用数组,必须声明一个引用数组的变量,并指明数组的元素类型。下卖弄是声明数组变量的语法:
- 7.2.2 创建数组
- 不同于声明基本数据类型变量,声明一个数组变量时并不给数组分配任何内存空间。它只是创建一个对数组的引用的存储位置。如果变量不包含对数组的引用,那么这个变量的值为null。除非数组已经被创建,否则不能给他分配任何元素。声明数组变量之后,可以使用下面的语法用new操作符创建数组,并且将它的引用赋给一个变量:
arrayRefVar = new elementType[arraySize];
- 这条语句做了两件事:
- 1、使用new elementType[arraySize]创建了一个数组;
- 2、把这个新创建的数组的引用赋值给变量arraySize。
- 声明一个数组变量、创建数组、将数组引用赋值给变量这三个步骤可以合并在一条语句里。
elementType[] arrayRefVar = new elementType[arraySize]; //或者 elementType arrayRefVar[] = new elementType[arraySize];
- 一个数组变量看起来似乎是存储了一个数组,但实际上他存储的是指向数组的引用。严格的讲,一个数组变量和一个数组是不同的,但多数情况下他们的差别是可以忽略的。因此,为了简化,通常常可以说myList是一个数组,而不用更长的陈述:myList是一个含有double型元素数组的引用变量。
- 不同于声明基本数据类型变量,声明一个数组变量时并不给数组分配任何内存空间。它只是创建一个对数组的引用的存储位置。如果变量不包含对数组的引用,那么这个变量的值为null。除非数组已经被创建,否则不能给他分配任何元素。声明数组变量之后,可以使用下面的语法用new操作符创建数组,并且将它的引用赋给一个变量:
- 7.2.3 数组大小和默认值
- 当给数组分配空间时,必须指定该数组能够存储的元素个数,从而确定数组大小。创建数组之后就不能再修改它的大小了。可以使用arrayRefVar.length得到数组的大小。
- 当创建数组后,他的元素呗赋予默认值,数值型基本数据类型的默认值为0,char型的默认值为‘\u0000’,boolean型的默认值为false。
- 7.2.4 访问数组元素
- 数组元素可以通过下标访问。数组下标是基于0的,也就是说,其范围从0开始到arrayRefVar.length-1结束。数组中的每个元素都可以使用下面的语法表示,称为下标变量:
arrayRefVar[index];
- 数组元素可以通过下标访问。数组下标是基于0的,也就是说,其范围从0开始到arrayRefVar.length-1结束。数组中的每个元素都可以使用下面的语法表示,称为下标变量:
- 7.2.5 数组初始化简写方式
- Java有一个简捷的标记,称作数组初始化简写方式,他使用下面的语法将声明数组、创建数组和初始化数组结合到一条语句中:
elementType[] arrayRefVar = {value0,value1,...,valuek};
- 数组初始化简写方式中不使用操作符new。使用数组初始化简写方式时,必须将声明、创建和初始化数组都放在一条语句中。
- Java有一个简捷的标记,称作数组初始化简写方式,他使用下面的语法将声明数组、创建数组和初始化数组结合到一条语句中:
- 7.2.6 处理数组
- 处理数组元素时,经常会用到for循环,理由如下:
- 数组中所有元素都是同一类型的。可以使用循环以同样的方式反复处理这些元素。
- 由于数组的大小是已知的,所以很自然的就使用for循环。
- 1、使用输入值初始化数组
double[] myList = new double[4]; java.util.Scanner input = new java.util.Scanner(System.in); System.out.print("Enter " + myList.length + " values: "); for (int i = 0;i < myList.length;i++) myList[i] = input.nextDouble();
- 2、使用随机数初始化数组
for (int i = 0;i < myList.length;i++){ myList[i] = Math.random() * 100; }
- 3、显示数组
char[] city = {'D','a','l','l','a','s'}; System.out.println(city);
- 4、对所有元素求和
double total = 0; for (int i = 0;i < myList.length;i++){ total += myList[i]; }
- 5、找出最大元素
double max = myList[0]; for (int i = 0;i < myList.length;i++){ if (myList[i] > max)max = myList[i]; }
- 6、找出最大元素的最小下标值
double max = myList[0]; int indexOfMax = 0; for (int i = 1;i < myList.length;i++){ max = myList[i]; indexOfMax = 1; }
- 7、随机打乱
for (int i = 0;i < myList.length - 1;i++){ int j = (int)(Math.random() * myList.length); double temp = myList[i]; myList[i] = myList[j]; myList[j] = temp; }
- 8、移动元素
double temp = myList[0]; for (int i = 1;i < myList.length;i++){ myList[i - 1] = myList[i]; } myList[myList.length - 1] = temp;
- 9、简化编码
String[] months = {"January","Februry",....,"December"}; System.out.print("Enter a month number (1 to 12): "); int monthNumber = input.nextInt(); System.out.println("The month is " + months[monthNumber - 1]);
- 处理数组元素时,经常会用到for循环,理由如下:
- 7.2.7 foreach循环
- Java支持一个简便的for循环,称为foreach循环,即不使用下标变量就可以顺序的遍历整个数组。下面的代码就可以显示数组myList的所有元素:
for(double e : myList){ Syetem.out.println(e) }
- foreachde的语法是:
for(elementType element: arrayRefVar){ //Process the element }
- Java支持一个简便的for循环,称为foreach循环,即不使用下标变量就可以顺序的遍历整个数组。下面的代码就可以显示数组myList的所有元素:
- 7.2.1 声明数组变量
-
7.3 示例学习:分析数字
- 编写一个程序,找到大于平均值的项的数目。
package chapter06; public class AnalyzeNumbers { public static void main(String[] args){ java.util.Scanner input = new java.util.Scanner(System.in); System.out.print("Enter the number of items: "); int n = input.nextInt(); double[] numbers = new double[n]; double sum = 0; System.out.print("Enter the numbers : "); for (int i = 0;i < n;i++){ numbers[i] = input.nextDouble(); sum += numbers[i]; } double average = sum / n; int count = 0; for (int i = 0;i < n;i++) if (numbers[i] > average) count++; System.out.println("Average is " + average); System.out.println("Number of elements above the average is " + count); } }
- 编写一个程序,找到大于平均值的项的数目。
-
7.4 示例学习:一副牌
package chapter06; public class DeckOfCards { public static void main(String[] args){ int[] deck = new int[52]; String[] suits = {"Spads","Hearts","Diamonds","clubs"}; String[] ranks = {"Ace","2","3","4","5","6","7","8","9","10","Jack","Queen","King"}; 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); } } }
-
7.5 复制数组
- 要将一个数组中的内容复制到另外一个数组中,需要将数组的每个元素复制到另外一个数组中。
list2 = list1
- 上面的语句并不能将list1引用的数组内容复制到list2,而只是将list1的引用值复制给了list2.在这条语句之后,list1和list2都指向同一个数组。list2原先所引用的数组不能再引用,它就变成了垃圾,会被Java虚拟机自动收回(这个过程称为垃圾回收)。
- 复制数组有三种方法:
- 1、使用循环语句逐个的赋值数组的元素;
- 2、使用System类中的静态方法arraycopy;
- arraycopy的语法是:
arraycopy(sourceArray,srcPos,targeArray,tarPos,length);
- 参数srcPos和tarPos分别表示在源数组sourceArray和目标数组targetArray中的起始位置。从sourceArray复制到targetArray中的元素个数由参数length指定。
- arraycopy方法没有给目标数组分配内存空间。复制前必须创建目标数组以及分配给他的内存空间。复制完成后,sourceArray和targetArray具有相同的内容,但占有独立的内存空间。
- arraycopy的语法是:
- 3、使用clone方法赋值数组。
- 要将一个数组中的内容复制到另外一个数组中,需要将数组的每个元素复制到另外一个数组中。
-
7.6 将数组传递给方法
- 当一个数组传递给方法时,数组的引用被传给方法。
- 和方法传递基本数据类型一样,也可以给方法传递数组。
public static void printArray(int[] array){ for(int i = 0;i < array.length;i++){ System.out.print(array[i] + " "); } }
- 使用下述语法创建数组:
new elementType[]{value0,value1,...,valuek};
- 该数组没有显式的引用变量,这样的数组称为匿名函数。
- Java使用按值传递的方式将实参传递给方法。传递剧本数据类型变量的值与传递数组值有很大的不同:
- 对于基本数据类型参数,传递的是实参的值;
- 对于数组类型参数,参数值是数组的引用,给方法传递的是这个引用。从语义上来讲,最好的描述为传递共享信息,即方法中的数组和传递的数组是一样的。因此,如果改变方法中的数组,将会看到方法外的数组也改变了。
package chapter06; public class TestPassArray { public static void main(String[] args){ int[] a = {1,2}; System.out.println("Before invoking swap"); System.out.println("array is {" + a[0] + ", " + a[1] + "}"); swap(a[0],a[1]); System.out.println("After invoking swap"); System.out.println("array is {" + a[0] + ", " + a[1] + "}"); } public static void swap(int n1,int n2){ int temp = n1; n1 = n2; n2 = temp; } public static void swapFirstTwoInArray(int[] array){ int temp = array[0]; array[0] = array[1]; array[1] = temp; } }
-
7.7 方法返回数组
- 当方法返回一个数组时,数组的引用被返回。
-
7.8 示例学习:统计每个字母出现的次数
package chapter06; public class CountLettersInArray { public static void main(String[] args){ char[] chars = createArray(); System.out.println("The lowercase letters are: "); displayArray(chars); int[] counts = countLetters(chars); System.out.println(); System.out.println("The occurrences of each letter are: "); displayCounts(counts); } public static char[] createArray(){ char[] chars = new char[100]; for (int i = 0;i < chars.length;i++) chars[i] = RandomCharacter.getRandomLowerCaseLetter(); return chars; } public static void displayArray(char[] chars){ for (int i = 0;i < chars.length;i++){ if ((i + 1) % 20 == 0) System.out.println(chars[i]); else System.out.println(chars[i] + " "); } } public static int[] countLetters(char[] chars){ int[] counts = new int[26]; for (int i = 0;i < chars.length;i++) counts[chars[i] - 'a']++; return counts; } public static void displayCounts(int[] counts){ for (int i = 0;i < counts.length;i++){ if ((i + 1) % 10 == 0) System.out.println(counts[i] + " " + (char)(i + 'a')); else System.out.println(counts[i] + " " + (char)(i + 'a') + " "); } } }
-
7.9 可变长参数列表
- 具有同样类型的数目可变的参数可以传递给方法,并将作为数组对待。方法中的参数声明如下:
typeName...patameterName(类型名...参数名)
- 在方法声明中,指定类型后紧跟着省略号(...)。只能个方法中指定一个可变长参数,同时该参数必须是最后一个参数。任何常规参数必须在他之前。
- Java将可变长参数当成数组对待。
package chapter06; public class VarArgsDemo { public static void main(String[] args){ } public static void printMax(double...numbers){ if (numbers.length == 0){ System.out.println("No argument passed"); return; } double result = 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); } }
- 具有同样类型的数目可变的参数可以传递给方法,并将作为数组对待。方法中的参数声明如下:
-
7.10 数组的查找
- 7.10.1 线性查找法
package chapter06; public class LineearSearch { public static int linearSearch(int[] list,int key){ for (int i = 0;i < list.length;i++){ if (key == list[i]) return i; } return -1; } }
- 7.10.2 二分查找法
package chapter06; 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; } }
- 二分查找法的前提是列表必须以升序排好序了。
- 7.10.1 线性查找法
-
7.11 数组的排序
package chapter06; public class SelectionSort { public static void selectiongSort(double[] list){ for (int i = 0;i < list.length;i++){ double currentMin = list[i]; int currentMinIndex = 1; for (int j = i + 1;j < list.length;j++){ if (currentMin > list[j]){ currentMin = list[j]; currentMinIndex = j; } } if (currentMin != 1){ list[currentMinIndex] =list[i]; list[i] = currentMin; } } } }
-
7.12 Arrays类
- java.util.Arrays类包含一些使用的方法用于常见的数组操作,比如排序和查找。
- 可以使用sort或者parrallelSort方法对整个数组或部分数组进行排序。
- 可以调用sort(number)对整个数组numbers排序。可以调用sort(chars,1,3)对从chars[1]到chars[3-1]的部分数组进行排序。如果你的计算机有多个处理器,那么parallelSort将更加高效。
double[] numbers = {6.0,4.4,1.9,2.9,3.4,3.5}; java.util.Arrays.sort(numbers); java.util.Arrays.parallelSort(numbers); char[] chars = {'a','A','4','F','D','P'}; java.util.Arrays.sort(chars,1,3); java.util.Arrays.parallelSort(chars,1,3);
- 可以采用二分查找法(binarySearch方法)在数组中查找关键字。如果数组中不存在关键字,方法返回-(insertIndex+1)。
int[] list = {2,4,7,10,11,45,50,59,60,66,69,70,79}; System.out.println("1. Index is " + java.util.Arrays.binarySearch(list,11)); System.out.println("2. Index is " + java.util.Arrays.binarySearch(list,12)); char[] chars1 = {'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'));
- 可以采用equals方法检测两个数组是否严格相等。
int[] list1 = {2,4,7,10}; int[] list2 = {2,4,7,10}; int[] list3 = {4,2,7,10}; System.out.println(java.util.Arrays.equals(list1,list2));//true System.out.println(java.util.Arrays.equals(list2,list3));//false
- 可以使用fill方法填充整个数组或部分数组。
int[] list1 = {2,4,7,10}; int[] list2 = {2,4,7,7,7,10}; java.util.Arrays.fill(list1,5); java.util.Arrays.fill(list2,1,5,8);
- 可以使用toString方法来返回一个字符串,该字符串代表了数组中的所有元素。
int[] list3 = {2,4,7,10}; System.out.println(java.util.Arrays.toString(list));
-
7.13 命令行参数
main方法的生命有点特殊,他具有String[]类型参数args。很明显,参数args是一个字符串数组。main方法就像一个带参数的普通方法。可以通过传递实参来调用一个普通方法。当然也是可以给main传递参数的。
- 7.13.1 向main方法传递字符串
- 运行程序时,可以从命令行给main方法传递字符串参数。例如,爱你的命令行用三个字符串arg0、arg1、arg2启动程序TestMain:
java TestMain arg0 arg1 arg2
- 其中,参数arg0、arg1、arg2都是字符串,但是在命令行中出现时,不需要放在双引号中。这些字符串用空格分隔。如果字符串包含空格,那就必须使用双引号括住。
- 当调用main方法时,Java解释器会创建一个数组存储命令行参数,然后将该数组的引用传递给args。例如,乳沟调用具有n个参数的程序,Java解释器创建一个如下所示的数组:
args = new String[n];
- 如果运行程序时没有传递字符串,那么使用new String[0]创建数组。在这种情况下,哎数组是长度为0的空数组。args是对这个空数组的引用。因此,args不是null,args.length为0。
- 运行程序时,可以从命令行给main方法传递字符串参数。例如,爱你的命令行用三个字符串arg0、arg1、arg2启动程序TestMain:
- 7.13.2 示例学习:计算器
package chapter06; public class Calculator { public static void main(String[] args){ if (args.length != 3){ System.out.println("Usage:java Calculator operand1 operator operand2"); System.exit(1); } int result = 0; switch (args[1].charAt(0)){ case '+':result = Integer.parseInt(args[0]) + Integer.parseInt(args[2]);break; case '-':result = Integer.parseInt(args[0]) - Integer.parseInt(args[2]);break; case '.':result = Integer.parseInt(args[0]) * Integer.parseInt(args[2]);break; case '/':result = Integer.parseInt(args[0]) / Integer.parseInt(args[2]);break; } System.out.println(args[0] + ' ' + args[1] + ' ' + args[2] + " = " + result); } }
- 我们使用.符号用于乘法,而不是通常的符号。原因是当符号用于命令行时表示当前目录下的所有文件。
- 7.13.1 向main方法传递字符串