目录
学习笔记
所有的项目开发中,都存在有数组的使用,下面介绍数组的基本概念与使用。
数组的基本概念
如果要定义一百个变量,使用基本数据类型可以定义,但是不方便定义与使用。在程序开发过程中考虑一组变量的整体维护,专门提供数组的定义。即数组的本质是相关变量的集合,需要注意:在Java中数组定义为引用数据类型,所以数组一定牵扯到内存的分配,即考虑到使用关键字new来处理,数组的定义格式:
- 数组的动态初始化:初始化过后,数组每个一个元素保存内容为其对应数据类型的默认值
|-声明并定义初始化数组:
|-数组类型 数组名称 [] = new 数据类型 [长度];
|-数组类型 [] 数组名称 = new 数据类型 [长度];
- 数组的静态初始化: 在数组定义时为其设置好定义内容;
|-简化格式:数据类型 数组名称 [] = {数据1,数据2,....}
|-完整格式:数据类型 数组名称 [] = new 数据类型 [] {数据1,数据2,....}
当创建数组后,按照如下方式进行访问:
- 使用脚标进行每个元素的定义,脚标从零开始,可以使用的脚标范围【0-数组长度-1】,如果超过范围,出现异常“ArrayInderOutOfBoundsException”数组越界。
- 使用数组是为了方便的变量管理,所以进行数组操作的时侯,往往会使用for循环来完成;
- 数组的长度可以使用“数组名称.length”属性来使用;
范例:数组动态初始化与使用
public class ArrayDemo{
public static void main(String args[]){
// 使用动态初始化
int data [] = new int [3] ;
data [0] = 10 ;
data [1] = 12 ;
data [2] = 57 ;
for(int i = 0; i < data.length; i++){
System.out.println(data[i]) ;
}
}
}
范例:数组的静态初始化
public class ArrayDemo{
public static void main(String args[]){
// 使用静态初始化
int data [] = new int [] {11, 23, 57} ;
for(int i = 0; i < data.length; i++){
System.out.println(data[i]) ;
}
}
}
数组的引用传递
通过上面的介绍可以发现,在数组使用过程中依然使用关键词new,进行内存空间的开辟,同理一定存在关系的匹配问题。
范例:定义一个简单代码
public class ArrayDemo{
public static void main(String args[]){
// 使用动态初始化
int data [] = new int [3] ;
data [0] = 10 ;
data [1] = 20 ;
data [2] = 30 ;
for(int i = 0; i < data.length; i++){
System.out.println(data[i]) ;
}
}
}
以上面程序为例,进行内存分析。
数组本身属于引用数据类型,就会发生引用传递,按照传统方式:一个堆内存可以被多个栈内存所引用。
范例:观察引用数据类型数组
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {10, 20, 30} ; // 静态初始化
int temp [] = data ; // 引用传递
temp [0] = 99 ;
for(int i = 0; i < data.length; i++){
System.out.println(data[i]) ;
}
}
}
对上述程序进行引用内存分析。
由于数组属于引用数据类型,所以一定要开辟堆内存空间之后才能使用,如果未开辟就使用,会出现“NullPointerException”异常。必须提供有实例化对象,才可以进行操作。
foreach迭代输出
对于的数组而言,一般使用for循环进行输出,但是在使用传统for循环输出的时候,都采用下形式进行访问。为了减轻下标处理不当引起的数组越界,所以参考了.NET中的设计,引用了一个增强型的for循环(foreach),利用foreach的语法结构可以直接获取里面的每一个元素避免下标结构。格式如下:
for(数据类型 变量:数组或集合 ){}
自动将数组中的每一个元素按照顺序取出,保存在变量中。这样就可以通过变量访问数组中的内容。
范例:使用foreach
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {10, 20, 30} ; // 静态初始化
for(int temp : data){
System.out.println(temp) ;
}
}
}
这种方法可以避免下标的控制
二维数组
在之前定义,只有一个"[]",所以这个时候就好像只有一行数据。
- 传统数据就好比一行数据,下标由系统维护;
下标 | 0 | 1 | 2 | 3 |
数据 | 10 | 20 | 30 | 40 |
- 需要多行多利,就需要两个下标描述一个数据,即行下标与列下标,即用两个“[]”;
下标 | 0 | 1 | 2 | 3 |
0 | 12 | 56 | 856 | 132 |
1 | 564 | 32 | 564 | 56 |
2 | 54 | 564 | 35 | 325 |
3 | 132 | 132 | 138 | 56 |
4 | 87 | 354 | 543 | 879 |
对于二维数组的定义语法结构如下:
- 数组的动态初始化:
|- 数据类型 数组名称 [][] = new 数据类型 [行个数][列个数];
- 数组的静态初始化:
|- 数据类型 数组名称 [][] = new 数据类型 [][] {{数据1,数据2,....},{数据1,数据2,...},......}
范例:定义一个二维数组
public class ArrayDemo{
public static void main(String args[]){
int data [][] = new int [2][3] ;
data [0][0] = 12 ;
data [0][1] = 22 ;
data [0][2] = 54 ;
data [1][0] = 23 ;
data [1][1] = 57 ;
data [1][2] = 75 ;
for (int i = 0; i < data.length ; i++ ){
for (int j = 0; j < data[0].length ; j++ ){
System.out.println("data[" + i + "]" + "[" + j +"]" + " = " + data[i][j]) ;
}
}
}
}
使用foreach输出
public class ArrayDemo{
public static void main(String args[]){
int data [][] = new int [2][3] ;
data [0][0] = 12 ;
data [0][1] = 22 ;
data [0][2] = 54 ;
data [1][0] = 23 ;
data [1][1] = 57 ;
data [1][2] = 75 ;
for (int temp [] : data ){
for ( int num : temp ){
System.out.println(num) ;
}
}
}
}
通过foreach可以观察到,二维数组就是数组的嵌套使用。随着技术的发展,如果要进行一些应用层的程序开发,那么很少涉及到二维数组。
数组与方法
对于引用数据类型主要的特点是可以与方法进行引用传递,数组本身属于引用数据类型,所以自然也可以通过引用的方法实现引用传递的操作。
范例:实现数组引用传递
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {1,2,3,4,5} ;
printArray(data) ; //传递数组
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
对于此时的引用传递,进行内存关系分析。
既然可以使用方法接受一个数组,也可以方法返回一个数组,即只需要在方法的返回值类型上控制;
public class ArrayDemo{
public static void main(String args[]){
int data [] = initArray() ; // 通过方法获取数组内容
printArray(data) ; //传递数组
}
public static int [] initArray(){
int arr [] = new int {1,2,3,4,5} ;
return arr ; //返回一个数组
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
数据与方法的内存分析
范例:通过方法修改数组内容
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {1,2,3,4,5} ;
changeArray(data) ;
printArray(data) ; //传递数组
}
public static void changeArray(int arr[]){
for (int i = 0; i < arr.length ; i ++ ){
arr[i] *= 2 ;
}
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
上述程序的内存关系。
案例:随意定义一个int数组内容,要求计算出数组的总和、最大值、最小值、平均值。
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {1,2,3,4,5} ;
int sum = 0 ;
double avg = 0.0 ;
int max = data[0] ;
int min = data[0] ;
for (int i = 0; i < data.length ; i ++ ){
sum += data[i] ;
if (data[i] > max){
max = data[i] ;
}
if (data[i] < min){
min = data[i] ;
}
}
avg = sum / data.length ;
System.out.println("数组总和:" + sum ) ;
System.out.println("数组平均值:" + avg ) ;
System.out.println("数组最大值:" + max ) ;
System.out.println("数组最小值:" + min ) ;
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
这个程序的问题,主方法所在的类被称为主类,主类肯定不希望涉及过于复杂的功能。在进行开发的过程中,主方法相当于客户端,而对于客户端的代码应该尽可能的简单,所以最好的做法是将这些操作放在程序类中执行。
范例:改善操作设计
class ArrayUtil{ // 是一个操作工具类
private int sum ; // 保存总和
private double avg ; // 保存平均值
private int max ; // 保存最大值
private int min ; // 保存最小值
// 这几个值通过计算得来,不需要构造方法构造属性
public ArrayUtil(int data[]){// 进行数组计算
this.sum = 0 ;
this.max = data[0] ;
this.min = data[0] ;
for (int i = 0; i < data.length ; i ++ ){
this.sum += data[i] ;
if (data[i] > this.max){
this.max = data[i] ;
}
if (data[i] < this.min){
this.min = data[i] ;
}
}
this.avg = this.sum / data.length ;
}
// setter略
public int getSum(){
return this.sum ;
}
public double getAvg(){
return this.avg ;
}
public int getMax(){
return this.max ;
}
public int getMin(){
return this.min ;
}
}
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {1,2,3,4,5} ;
ArrayUtil util = new ArrayUtil(data) ; //数据计算
System.out.println("数组总和:" + util.getSum() ) ;
System.out.println("数组平均值:" + util.getAvg() ) ;
System.out.println("数组最大值:" + util.getMax() ) ;
System.out.println("数组最小值:" + util.getMin() ) ;
}
}
此时的主类,就像电脑一样,只关心操作,而具体的操作被类封装了。它就像一个组件。
数组排序案例分析
数组排序,将杂乱的数组按照顺序存放,但是数组的排序总是通过一个基础的模型完成的,例如:先通过升序排序的方式进行处理。
范例:数组排序分析
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {8,9,0,2,3,5,10,9,7,6, 1} ;
for (int k = 0; k < data.length ; k++){
for (int i = 0; i < data.length-1-k; i++ ){
if (data[i] > data[i+1]){
int temp = data[i] ; // 注意temp的定义
data[i] = data[i+1] ;
data[i+1] = temp ;
}
}
}
printArray(data) ;
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
以上的代码,都是通过主类完成的,不符合于面向对象的操作。需要交由类进行操作;
class ArrayUtil{
public static void sort(int data[]){ // 进行数组的排序处理,如果不在static就需要一个实例化对象去调用这个方法,加上就可以类名调用。
for (int k = 0; k < data.length ; k++){
for (int i = 0; i < data.length-1-k; i++ ){
if (data[i] > data[i+1]){
int temp = data[i] ; // 注意temp的定义
data[i] = data[i+1] ;
data[i+1] = temp ;
}
}
}
}
}
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {8,9,0,2,3,5,10,9,7,6, 1} ;
ArrayUtil.sort(data) ;
printArray(data) ;
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
以后进行类设计的时候,如果发现类中没有发现属性存在的意义,那么定义的方法就没有必要使用普通方法了,因为普通方法需要实例化对象产生之后才能调用。而现在不需要实例化对象。
数组的反转操作
反转操作即进行首尾交换。具有两种方法,如下:
方法一:即新建一个数组,逆序存放,
进行内存分析。
会产生垃圾空间。
方法二:在一个数组上转置
数组转换的次数:int类型,总长度/2,并不需要考虑奇数个数与偶数个数。操作如下:
class ArrayUtil{
public static void reverse(int data[]){
int center = data.length / 2 ;
for (int i = 0; i < center ; i++ ){
int temp = data[i] ;
data[i] = data[data.length -1 - i] ;
data[data.length -1 - i] = temp ;
}
}
}
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {1,2,3,4,5,6,8,9} ;
ArrayUtil.reverse(data) ;
printArray(data) ;
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
数组相关操作方法
由于数组是一个重要概念,所以Java本身也提供数组的相关支持和处理,这些处理也在开发中常用。
1、数组排序:java.util.Arrays.sort(数组名称)
public class ArrayDemo{
public static void main(String args[]){
int data [] = new int [] {11,223,5663,564,25,46,368,9} ;
java.util.Arrays.sort(data) ;
printArray(data) ;
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
2、数组拷贝(把方法做了一些变形):
- System.arraycopy(原数组名称,原数组起点,目标数组名称,目标数组起始点,拷贝长度)。
范例:实现数组拷贝
public class ArrayDemo{
public static void main(String args[]){
int dataA [] = new int [] {1,2,3,4,5,6,7,8,9} ;
int dataB [] = new int [] {11,22,33,44,55,66,77,88,99} ;
System.arraycopy(dataA, 5, dataB, 3, 3) ;
printArray(dataB) ;
}
//要求接受一个int型的数组
public static void printArray(int temp []){
for (int i=0 ; i < temp.length ; i++ ){
System.out.println(temp[i]) ;
}
}
}
这些操作都是系统本身提供的,即:你可以在开发中使用的操作,也可以自己写,但需要考虑所有数据类型。
方法可变参数
如果要求定义一个方法,实现任意多个整型数据的相加处理。最早时候只能通过数组处理。
class ArrayUtil{
public static int sum(int data[]){
int sum = 0 ;
for (int i = 0; i < data.length ; i ++){
sum += data[i] ;
}
return sum ;
}
}
public class ArrayDemo{
public static void main(String args[]){
System.out.println( ArrayUtil.sum( new int [] {1,2,3})) ;
}
}
虽然以上的程序可以实现任意多个数字的参数内容传递,但是与实际的内容不相符合。实际要求传递任意多个参数,而不是一个数组。为了方便开发者进行可变参数的定义,提供多个参数的传递。
范例:采用可变参数(将[]去掉,加三点)
class ArrayUtil{
public static int sum(int ... data){ // 变种数组
int sum = 0 ;
for (int i = 0; i < data.length ; i ++){
sum += data[i] ;
}
return sum ;
}
}
public class ArrayDemo{
public static void main(String args[]){
System.out.println( ArrayUtil.sum( new int [] {1,2,3})) ;
System.out.println( ArrayUtil.sum(1,2,3)) ;
}
}
在以后进行一些程序设计和开发者调用的时候,利用此种形式就可以避免数组的传递操作,可变参数的本质:依然是数组。
对象数组
在之前接触到的都是一些基本数据类型定义的数组,但是在Java的程序本身各种数据类型,都可以成为数组类型。类也可以成为数组类型,这样的数组称为对象数组,对象数组的定义如下:
-
动态初始化:类 对象数组名称 [] = new 类 [数组长度] ,每一个元素都是空;
-
静态初始化:类 对象数组名称 [] = new 类 [] {实例化对象1、实例化对象2、....} ;
范例:使用动态初始化定义对象数组
class Person{
private String name ;
private int age ;
public Person(String name, int age){
this.name = name ;
this.age = age ;
}
public String getInfo(){
return "姓名:" + this.name +
"、年龄:" + this.age ;
}
// setter getter略
}
public class ArrayDemo{
public static void main(String args[]){
Person per [] = new Person [3] ; // 对象数组
per[0] = new Person("张三", 20) ;
per[1] = new Person("李四", 20) ;
per[2] = new Person("王五", 20) ;
for (int i = 0; i < per.length ; i ++){
System.out.println(per[i].getInfo()) ;
}
}
}
范例:对象数组的静态初始化
class Person{
private String name ;
private int age ;
public Person(String name, int age){
this.name = name ;
this.age = age ;
}
public String getInfo(){
return "姓名:" + this.name +
"、年龄:" + this.age ;
}
// setter getter略
}
public class ArrayDemo{
public static void main(String args[]){
Person per [] = new Person []{
new Person("张三", 20), new Person("李四", 20), new Person("王五", 20)
} ; // 对象数组
for (int i = 0; i < per.length ; i ++){
System.out.println(per[i].getInfo()) ;
}
}
}
对于对象数组而言,只是更换了一下,数组的定义的数据类型,但是内存图比较不同。
所有的开发都不可能离开对象数组,但是通过一系列分析,也要知道数组中的最大缺陷:长度是固定的,数据线性保存,根据索引访问,速度较快(时间复杂度是1);