一 一维数组及内存分析
1.1 一维数组
数组可以看成是多个相同类型数据组合,对这些数据的统一管理。
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量
数组中的元素可以是任何数据类型,包括基本类型和引用类型
1.1.1 一维数组的声明方式
type var[];
或者
type[] var;
例如:
int a1[];
int a2[];
double b[];
Person[] p1;
String s1[];
Java语言中声明数组时不能指定其长度(数组中元素的个数),例如:
int a[5]; //非法
1.1.2 数组对象的创建
Java中使用关键字new创建数组对象,格式为:
数组名 = new 数组元素的类型 [数组元素的个数]
例如:
public class Test {
public static void main(String[] args) {
int[] s;
s = new int[5];
for (int i = 0; i < 5; i++) {
s[i] = i;
}
}
}
内存分析
1.1.2.1 程序入口
程序入口main()方法,引用类型为int,数组s,没有赋值,所以为空值。其在内存中结构如下:
1.1.2.2 程序继续往下执行
程序继续往下执行,s = new int[5];其中s为一个数组名,对象有5个元素,内存分布图如下:
1.1.2.3 程序继续往下执行
程序继续往下执行,for (int i = 0; i < 5; i++) { s[i] = i; }
for循环,其中s[i]的i表示下标,下标从0开始,s[0]表示第一个元素,s[1]表示第二个元素,依次增加。所以把i的值复制给s[i],当i为0时,第一个元素为0,当i为1时,第二个元素为1,依次循环。所以内存数组s的元素分布如下所示:
当调用的时候,s[0]的值(元素)为0,s[3]的元素为3
1.1.3 元素为引用数据类型的数组
注意:元素为引用数据类型的数组中的每一个元素都需要实例化
如下一段代码
public class Test {
public static void main(String[] args) {
Date[] days;
days = new Date[3];
for (int i=0;i<3;i++) {
days[i] = new Date(2016,10,i+1);
}
}
}
class Date {
int year;
int month;
int day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
}
其内存分布如下图所示:
所以当调用days[0]的时候,为2016,10,1;days[2]的时候为2016,10,3
二 数组元素的创建和使用
2.1 数组初始化
2.1.1 动态初始化
动态初始化:数组的定义与为数组元素分配空间和赋值的操作分开进行
如下面一段代码
public class Test {
public static void main(String[] args) {
int a[];
a = new int[3];
a[0] = 3;
a[1] = 9;
a[2] = 8;
Date days[];
days = new Date[3];
days[0] = new Date(1,10,2016);
days[1] = new Date(2,10,2016);
days[2] = new Date(3,10,2016);
}
}
class Date {
int year,month,day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
}
2.1.2 静态初始化
静态初始化:在定义数组的同时就为数组元素分配空间并赋值
例如下面一段代码
public class Test {
public static void main(String[] args) {
int a[] = {3 , 9 , 8};
Date days[] = {
new Date(1,10,2016) ;
new Date(2,10,2016);
new Date(3,10,2016);
}
}
}
class Date {
int year,month,day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
}
2.1.3 数组元素的默认初始化
数组是引用类型,它的元素相当于类的成员变量,因此数组分配空间后,每个元素也被按照成员变量的规则被隐式初始化。
如下面一段代码
public class Test {
public static void main(String[] args) {
int a[] = new int[5];
Date days[] = new Date[3];
System.out.println(a[3]);
System.out.println(days[2]);
}
}
class Date {
int year,month,day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
}
2.2 数组元素的引用
定义并用运算符new为之分配空间后,才可以引用数组中的每个元素,数组元素的引用方式为:
arrayName[index]
- index为数组元素的下标,可以是整型常量或整型表达式。如
- a[3] b[i] c[6*i]
- 数组元素下标从0开始;长度为n的数组的合法下标取值范围为:0 - n-1
- 每个数组都有一个属性length指明它的长度,例如
- a.length的值为数组a的长度(元素个数)
如下面一段代码
public class TestArray {
public static void main(String[] args) {
int a[] = {1, 3, 5, 4, 8, 9, 2, 7, 6};
for ( int i=0;i<a.length;i++) {
if (i == (a.length - 1)) {
System.out.print(a[i]);
} else {
System.out.print(a[i] + "、");
}
}
}
}
此时就可以解释String[] args了,就是一个数组,main()方法的String数组,是为了保存后面内容的,可以这样验证这个数组
public class TestString {
public static void main(String[] args) {
for (int i=0;i<args.length;i++) {
System.out.println(args[i]);
}
}
}
javac编译完成以后,java运行加上参数即可。如java TestString 12 45 gh
三 练习
3.1 练习1
利用数组写一个计算器
public class TestArgs {
public static void main(String[] args) {
if (args.length<3) {
System.out.println("Usage: java TestArgs \"n1\" \"op\" \"n2\"");
System.exit(-1); //退出程序,并返回状态码-1
}
double d1 = Double.parseDouble(args[0]);
double d2 = Double.parseDouble(args[2]);
double d = 0;
if (args[1].equals("+")) {
d = d1 + d2;
} else if (args[1].equals("-")) {
d = d1 - d2;
} else if (args[1].equals("x")) {
d = d1 * d2;
} else if (args[1].equals("/")) {
d = d1 / d2;
} else {
System.out.println("Error operator");
System.exit(-1);
}
System.out.println(d);
}
}
其中double d1 = Double.parseDouble(args[0]);在Java中,将每个字符串类型的值都可以封装成一个对象,如Float{}对象,对象中的parseDouble()方法表示返回一个新的doule类型的值,数据静态方法。
3.2 练习2
给出如下数组,将数组的元素进行排序
int[] a = {2, 4, 6, 7, 3, 5, 1, 9, 8};
利用java程序完成操作
public class TestOrder {
public static void main(String[] args) {
int[] a = {2, 4, 6, 7, 3, 5, 1, 9, 8};
sort(a);
print(a);
}
//选择排序算法
private static void sort(int[] a) {
for (int i=0;i<a.length;i++) {
for (int j=i+1;j<a.length;j++) {
if (a[i] > a[j]) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}
//冒泡排序算法
private static void rank(int[] a) {
for (int i=0;i<a.length;i++) {
for (int j=0;j<a.length-1;j++) {
if (a[j]>a[j+1]) {
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}
}
private static void print(int[] a) {
for (int i=0;i<a.length;i++) {
System.out.print(a[i] + " ");
}
}
}
冒泡算法:
- 整个数列分为两个部分,无序序列和有序序列
- 初始状态为无序数列,如果让数列有序,最多需要n-1次循环比较,最少1次
- 每趟循环,无序数列中的最大值都会加入到有序数列,即每次无序数列减1,而有序的数列元素加1
- 元素之间进行比较,两两交换,最多需要交换n-1次
算法优化:
- 中间变量提升到循环语句之外,避免内存空间的频繁的申请和关闭,影响效率
- 每次元素间进行两两比较,有序数列增加1,而无序数列减少1,实际需要比较的次数与循环的趟数成反比,所以循环的次数是length-1-i
- 对于有些序列,当执行到n-2趟的时候,就已经排好序了,所以判断其如果排好序了,break即可
public class BubbleSort {
public static void main(String[] args) {
int[] a = {10,20,60,50,70,40,30};
bubbleSort(a);
print(a);
}
public static void bubbleSort(int[] a) {
int tmp;
boolean flag;
int i;
for (i=0;i<a.length;i++) {
flag = false;
for (int j=0;j<a.length-1-i;j++) {
if (a[j]>a[j+1]) {
tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true;
}
}
if (!flag) {
break;
}
}
}
public static void print(int[] a) {
for (int i=0;i<a.length;i++) {
System.out.print(a[i] + " ");
}
}
}
3.3 练习3
阅读下面程序,任意初始化days数组,然后对days的元素排序
public class SortObject {
public static void main(String[] args) {
Date[] days = new Date[5];
days[0] = new Date(2006,5,4);
days[1] = new Date(2006,7,4);
days[2] = new Date(2008,5,4);
days[3] = new Date(2004,5,9);
days[4] = new Date(2004,5,4);
}
}
class Date {
int year,month,day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
public int compare(Date date) {
return year > date.year ? 1
: year < date.year ? -1
: month > date.month ? 1
: month < date.month ? -1
: day > date.day ? 1
: day < date.day ? -1 : 0;
}
}
程序
public class SortObject {
public static void main(String[] args) {
Date[] days = new Date[5];
days[0] = new Date(2006,5,4);
days[1] = new Date(2006,7,4);
days[2] = new Date(2008,5,4);
days[3] = new Date(2004,5,9);
days[4] = new Date(2004,5,4);
bubbleSort(days);
print(days);
}
public static void bubbleSort(Date[] a) {
for (int i=0;i<a.length;i++) {
for (int j=0;j<a.length-1;j++) {
if (a[j].compare(a[j+1]) > 0) {
Date tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}
}
private static void print(Date[] a) {
for (int i=0;i<a.length;i++) {
System.out.println(a[i]);
}
}
}
class Date {
int year,month,day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
public int compare(Date date) {
return year > date.year ? 1
: year < date.year ? -1
: month > date.month ? 1
: month < date.month ? -1
: day > date.day ? 1
: day < date.day ? -1 : 0;
}
public String toString() {
return year + "-" + month + "-" + day;
}
}
3.4 练习4
数三退一,现在有500个人,500个人手拉手围成一个圈,从某一个人开始数数,当数到3的时候这个人就退出,继续从1开始数,每当有人数到3就退出。找出最后剩下的人是在什么位置上
public class Count3Quit {
public static void main(String[] args) {
boolean[] a = new boolean[500];
for (int i=0;i<a.length;i++) {
a[i] = true;
}
int len = a.length;
int countNum = 0;
int index = 0;
while (len>1) {
if (a[index] == true) {
countNum ++;
if (countNum == 3) {
countNum = 0;
a[index] = false;
len --;
}
}
index ++;
if (index == a.length) {
index = 0;
}
}
for (int i=0;i<a.length;i++) {
if (a[i] == true) {
System.out.println(i);
}
}
}
}
分析:首先可以这样考虑,如果人还在这个位置为true,如果人不在这个位置则变成false;所以可以设置boolean类型的数组,boolean[] a = new boolean[500];按照要求有500人,所以设置boolean的数组为500个元素,初始状态500个人围成一个圈,所以500个人都是true状态,所以初始化数组元素为true:
for (int i=0;i<a.length;i++) {
a[i] = true;
}
当数组元素的个数还大于1的时候,一定会循环,所以while条件为数组元素长度>1;由于数1 2 3,所以需要一个计数器,计数器初始值为0,int countNum = 0;当位置为true状态时,则计数器自加;计数器为3时,则这个人退出,即这个人设置为false,同样元素减1,计数器初始化为0;
这样循环,循环到剩下最后一个人的时候,则打印
3.5 练习5
搜索算法,搜索往往是建立在排好顺序的基础之上。
搜索算法有很多,例如二分算法。
原理:例如
1 2 3 4 5 6 7 8 9 10
先将数排好顺序,然后从中间或者靠近中间的位置开始分,左边一份,右边一份;如果需要查找的数大于这个中间的数,则往右边查找;反之则往左边查找。
实现
如数组int a[] = { 1, 3, 6, 8, 9, 10, 12, 18, 20, 34 };利用二分法进行查找是否有12这个数值,如果有则显示其位置
public class BinarySearch {
public static void main(String[] args) {
int a[] = { 1, 3, 6, 8, 9, 10, 12, 18, 20, 34 };
int i = 10;
System.out.println(binSear(a,i));
}
public static int binSear(int[] a,int num){
if (a.length == 0) {
return -1;
}
int startPos = 0;
int endPos = a.length - 1;
int m = (startPos + endPos) / 2;
while (startPos <= endPos) {
if (num == a[m]) {
return m;
}
if (num > a[m]) {
startPos = m + 1;
}
if (num < a[m]) {
endPos = m - 1;
}
m = (startPos + endPos) / 2;
}
return -1;
}
}
3.6 练习6
如题,练习3,先使用冒泡算法将对象类型进行排序,然后使用二分法进行查找是否有2006,5,4,如果有,显示其位置
public class SortObject {
public static void main(String[] args) {
Date[] days = new Date[5];
days[0] = new Date(2006,5,4);
days[1] = new Date(2006,7,4);
days[2] = new Date(2008,5,4);
days[3] = new Date(2004,5,9);
days[4] = new Date(2004,5,4);
}
}
class Date {
int year,month,day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
public int compare(Date date) {
return year > date.year ? 1
: year < date.year ? -1
: month > date.month ? 1
: month < date.month ? -1
: day > date.day ? 1
: day < date.day ? -1 : 0;
}
}
程序
public class SortObject {
public static void main(String[] args) {
Date[] days = new Date[5];
days[0] = new Date(2006,5,4);
days[1] = new Date(2006,7,4);
days[2] = new Date(2008,5,4);
days[3] = new Date(2004,5,9);
days[4] = new Date(2004,5,4);
Date d = new Date(2006,5,4);
bubbleSort(days);
print(days);
System.out.println(search(days,d));
}
public static void bubbleSort(Date[] a) {
for (int i=0;i<a.length;i++) {
for (int j=0;j<a.length-1;j++) {
if (a[j].compare(a[j+1]) > 0) {
Date tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}
}
private static int search(Date[] a,Date day) {
if (a.length == 0) {
return -1;
}
int startPos = 0;
int endPos = a.length - 1;
int m = (startPos + endPos) / 2;
while (startPos <= endPos) {
if (day.compare(a[m]) == 0) {
return m;
}
if (day.compare(a[m]) > 0) {
startPos = m + 1;
}
if (day.compare(a[m]) < 0) {
endPos = m - 1;
}
}
return -1;
}
private static void print(Date[] a) {
for (int i=0;i<a.length;i++) {
System.out.println(a[i]);
}
}
}
class Date {
int year,month,day;
Date(int y,int m,int d) {
year = y;
month = m;
day = d;
}
public int compare(Date date) {
if (year > date.year) {
return 1;
} else if (year < date.year) {
return -1;
} else if (month > date.month) {
return 1;
} else if (month < date.month) {
return -1;
} else if (day > date.day) {
return 1;
} else if (day < date.day) {
return -1;
} else {
return 0;
}
}
public String toString() {
return year + "-" + month + "-" + day;
}
}
四 二维数组
4.1 二维数组
二维数组可以看成以数组为元素的数组,例如
int a[][] = {{1,2}, {3,4,5,6}, {7,8,9}};
Java中多维数组的声明和初始化按从高维到低维的顺序进行。例如:
int a[][] = new int[3][];
a[0] = new int[2];
a[1] = new int[4];
a[2] = new int[3];
int t1[][] = new int[][4]; //非法
4.2 二维数组初始化
4.2.1 静态初始化
int intA[][] = {{1,2}, {2,3}, {3,4,5}};
int intB[3][2] = {{1,2}, {2,3}, {4,5}}; //非法
4.2.2 动态初始化
int a[][] = new int[3][5];
int b[][] = new int[3][];
b[0] = new int[2];
b[1] = new int [3];
b[2] = new int[5];
例如
一个二维数组int a[][] = {{1,2}, {3,4,5,6}, {7,8,9}};进行调用
public class Array2 {
public static void main(String[] args) {
int a[][] = {{1,2}, {3,4,5,6}, {7,8,9}};
for (int i=0;i<a.length;i++) {
for (int j=0;j<a[i].length;j++) {
System.out.print(a[i][j] + " ");
}
}
}
}
例如
public class Test {
public static void main(String[] args) {
String[][] s;
s = new String[3][];
s[0] = new String[2];
s[1] = new String[3];
s[2] = new String[2];
for (int i=0;i<s.length;i++) {
for (int j=0;j<s[i].length;j++) {
s[i][j] = new String("我的位置是: " + i + "," + j);
}
}
for (int i=0;i<s.length;i++) {
for (int j=0;j<s[i].length;j++) {
System.out.print(s[i][j] + " ");
}
System.out.println();
}
}
}
4.3 数组的拷贝
- 使用java.lang.System类的静态方法
poublic static void arraycopy (Object src,int srcPos,Object dest,int destPos,int length)
- 可以用于数组src从第srcPos项元素开始的length个元素拷贝到目标数组从destPos项开始的length个位置
- 如果源数据数目超过目标数组边界会抛出IndexOutOfBoundsException异常
如下面一段代码
public class TestArrayCopy {
public static void main(String[] args) {
String[] s = {"Microsoft","IBM","Sun","Oracle","Apple"};
String[] sBak = new String[6];
System.arraycopy(s,0,sBak,0,s.length);
for (int i=0;i<sBak.length;i++) {
System.out.print(sBak[i] + " ");
}
}
}
上面代码内存分布如下所示:
例如下面一段代码
public class TestArrayCopy {
public static void main(String[] args) {
int[][] intArray = {{1,2}, {1,2,3}, {3,4}};
int[][] intArrayBak = new int[3][];
System.arraycopy(intArray,0,intArrayBak,0,intArray.length);
intArrayBak[2][1] = 100;
for (int i=0;i<intArray.length;i++) {
for (int j=0;j<intArray[i].length;j++) {
System.out.print(intArray[i][j] + " ");
}
System.out.println();
}
}
}
上面代码的内存图如下所示:
最后最后调用intArray[2][1]的时候是100;
五 数组的基本操作
java.util包的Arrays类包含了用来操作数组的各种方法
5.1 填充替换数组元素
数组中的元素定义完成后,可通过Arrays类的静态方法fill()来对数组中的元素进行替换。
5.1.1 fill(int[] a, int value)
以int类型为例,该方法重载参数有char、boolean等都可以。
该方法可将指定的int值分配给int型数组的每个元素。
- a:要进行元素替换的数组
- value:要存储数组中所有元素的值
如下面一段代码
package com.bianjf.test;
import java.util.Arrays;
public class Swap {
public static void main(String[] args) {
int arr[] = new int[5];
Arrays.fill(arr, 8); //使用数字8对数组所有元素填充
for (int i = 0; i < arr.length; i++) {
System.out.println("第" + i + "个元素时:" + arr[i]);
}
}
}
结果显示arr数组所有的元素为8
1.1.2 fill(int[] a, int fromIndex, int toIndex, int vlaue)
该方法指定int值分配给int型数组指定范围中的每个元素。填充的范围从索引fromIndex(包含)一直到索引toIndex(不包含),如果fromIndex==toIndex,则填充范围为空
- a:要进行填充的数组
- fromIndex:要使用指定值填充的第一个元素的索引(包括)
- toIndex:要使用指定值填充的最后一个元素的索引(不包括)
- value:要存储在数组所有元素中的值
如下面一段代码
package com.bianjf.test;
import java.util.Arrays;
public class Displace {
public static void main(String[] args) {
int arr[] = new int[]{45, 12, 2, 10};
Arrays.fill(arr, 1, 2, 8); //将索引为1到索引为2(不包含)值替换成数值8
for (int i = 0; i < arr.length; i++) {
System.out.println("第" + i + "个元素是:" + arr[i]);
}
}
}
输出结果为45,8,2,10
索引为1的值为12, 将12替换成8
5.2 对数组进行排序
通过Arrays类的静态sort()方法可以实现对数组的排序。sort()方法可以对任意类型的数组进行升序排序
Arrays.sort(Object[] obj)
如下面一段代码
package com.bianjf.test;
import java.util.Arrays;
public class Taxis {
public static void main(String[] args) {
int arr[] = new int[]{23, 42, 12, 8};
Arrays.sort(arr);//升序排序
for (int i : arr) {
System.out.println(i);
}
}
}
5.3 复制数组
Arrays类的copyOf()方法与copyOfRange()方法可以实现对数组的复制。copyOf()方法是复制数组至指定的长度。copyOfRange()方法则将指定数组的指定长度复制到一个新的数组中。
5.3.1 copyOf(Object[] obj, int newLength)
copyOf()方法有多种重载形式,obj可以为int数组、float数组等任意数组。
- obj:要进行复制的数组
- newLength:指复制后的新数组的长度。如果新数组长度大于obj的长度,则用0填充。如果复制后的数组长度小于数组obj的长度,则会从obj数组的第一个元素开始截取至满足新数组长度为止。
如下代码
package com.bianjf.test;
import java.util.Arrays;
public class Cope {
public static void main(String[] args) {
int arr[] = new int[]{23, 42, 12};
int[] newArr = Arrays.copyOf(arr, 5);
for (int i : newArr) {
System.out.println(i);
}
}
}
输出结果为最后两个元素为0
5.3.2 copyOfRange()方法
该方法同样提供了多种重载形式。
copyOfRange(Object[] arr, int fromIndex, int toIndex)
- arr:要进行复制的数组
- fromIndex:指定开始复制数组的索引位置。fromIndex必须在0至整个数组的长度之间。新数组包括索引是fromIndex的元素
- toIndex:要复制范围的最后索引位置。可大于数组arr的长度。新数组不包括索引是toIndex的元素
如下面一段代码
package com.bianjf.test;
import java.util.Arrays;
public class Repeat {
public static void main(String[] args) {
int[] arr = new int[]{23, 42, 12, 84, 10};
int[] newArr = Arrays.copyOfRange(arr, 0, 3);
for (int i : newArr) {
System.out.println(i);
}
}
}
复制数组0-3索引的元素,所以输出为23,42,12
5.4 数组查询
Arrays类的binarySearch()方法,可使用二分搜索法来搜索指定数组,以获得指定对象。该方法返回要搜索元素的索引值。binarySearch()方法提供了多种重载形式,用于满足各种类型数组的查找需要。
5.4.1 binarySearch(Object[] a, Object key)
- a:要搜索的数组
- key:要搜索的值
如果key包含在数组中,则返回搜索值的索引。
否则返回“-1”或者“-”,如下面一段代码
package com.bianjf.test;
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
int[] ia = new int[]{1, 8, 9, 4, 5};
Arrays.sort(ia); //排序
int index = Arrays.binarySearch(ia, 4);
System.out.println("4的索引位置是:" + index);
}
}
因为已经排序,所以搜索索引位置为1
5.4.2 binarySearch(Object[] a, int fromIndex, int toIndex, Object key)
该方法在指定的范围内检索某一元素
- a:要进行检索的数组
- fromIndex:指定范围的开始出索引(包含)
- toIndex:指定范围的结束处索引(不包含)
- key:要搜索的元素
如下面一段代码
package com.bianjf.test;
import java.util.Arrays;
public class Rakel {
public static void main(String[] args) {
String[] str = new String[]{"ab", "cd", "ef", "yz"};
Arrays.sort(str); //排序
int index = Arrays.binarySearch(str, 0, 2, "cd");
System.out.println("cd的索引位置是:" + index);
}
}
输出结果为1