一維數組


單個的數組變量可以引用一個大的數據集合。

  在程序執行過程中,經常需要存儲大量的數據,例如,假設需要讀取某科100位學員的成績,計算它們的平均成績,然后找出有多少個學員成績大於平均值。首先,程序需要讀入這些數並且計算它們的平均值,然后將每個數與平均值進行比較判斷它是否大於平均值。為了完成這個任務,必須將全部的數據存儲到變量中。必須聲明100個變量,並且重復書寫100次幾乎完全相同的代碼。這樣編寫程序的方式是不太現實的,那么該如何解決這個問題呢?
  這就需要高效有條理的方法——數組:數組是編程語言中最常見的一種數據結構,可以用它來存儲一個元素個數固定且元素類型相同的有序集,每個數組元素存放一個數據,通常可通過數組元素的索引來訪問數組元素,包括為數組元素賦值和取出數組元素的值。在現在這個例子中,可以將所有的100位學員的成績存儲在一個數組中,並且通過一個數組變量訪問它。

一旦數組被創建,它的大小是固定的。使用一個數組引用變量,通過下標來訪問數組中的元素

  數組是用來存儲數據的集合,但是,java的數組要求所有的數組元素具有相同的類型。因此,在一個數組中,數組元素的類型是唯一的,即一個數組里只能存儲一種數據類型的數據,而不能存儲多種數據類型的數據。無須聲明單個變量,例如:score0, score1,...score99,只要聲明一個數組變量scores,並且用scores[0], scores[1],...scores[99]來表示單個變量(成績)。

1、聲明數組

  為了在程序中使用數組,必須聲明一個引用數組的變量,並指明數組的元素類型

    語法:elementType[] arrayName;(數據類型[] 數組引用變量)

  elementType可以是任意數據類型,但是數組中所有的元素都必須具有相同的數據類型。

  注意:因為java是面向對象的語言,而類與類之間可以支持繼承關系,這樣可能產生一個數組里可以存放多種數據類型的假象(參考后面合唱團例子)。例如有一個水果數組,要求每個數組元素都是水果,實際上數組元素既可以是蘋果,也可以是是banana(蘋果、香蕉都繼承了水果,都是一種特性的水果),但這個數組的數組元素的類型還是唯一的,只能是水果類型。

  從上面語法中看出,數組也是一種數據類型,它本身是一種引用數據類型。例如int是一個基本數據類型,但int[]就是一種引用數據類型。

   聲明數組時不能指定數組的長度

2、數組初始化

  不同於基本數據類型變量的聲明,聲明一個數組變量時並不在內存中給數組分配任何空間。數組是一種引用類型的變量,因此使用它定義一個變量時,僅僅表示定義了一個引用變量(也就是定義了一個指針),這個引用變量還沒有指向任何有效的內存,因此定義數組時不能指定數組的長度。而且由於定義數組只是定義了一個引用變量,並未指向任何有效的內存空間,所以還沒有內存空間來存儲數組元素,因此這個數組也不能使用,只有對數組進行初始化后才能使用(如果變量不包含對數組的引用,那么這個變量的值為null。除非數組已經被創建,否則不能給它分配任何元素)。

  初始化數組有兩種方式

              1)靜態初始化:初始化數組和給數組賦值同時完成,在開辟數組的空間時依據已給定的元素個數來開辟相應的空間數。    

      a.靜態初始化的第一種方式:

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

      b.靜態初始化的第二種方式:Java有一個簡捷的標記,稱作數組初始化語法,它使用下面的語法將聲明數組、創建數組和初始化數組結合到一個語句中:

        elementType[] arrayName={value0,value1,value2,...,valuek};(元素類型[] 數組引用變量={值1,值2,...,值k};)

      說明數組在定義時就完成了空間的開辟和賦值;注意:使用該語法時,必須聲明、創建和初始化數組都放在一條語句中。將他們分開會產生語法錯誤。因此,下面的語句是錯誤的:。

        int a[];
        a = {10,20,30};//這不行

              2)動態初始化:只告知數組需要幾個存儲空間,但是並未決定要存哪些值,由系統為每個元素指定初始值。語法格式如下:

      arrayName = new type[length];

//數組的聲明和初始化同時完成,使用動態初始化語法
int[] prices = new int[5];
//數組的聲明和初始化同時完成,初始化數組時元素的類型是定義數組時元素的子類
Object[] books = new String[5];   

              注意:a.做動態初始化時必須指明數組的元素個數;

                         b.數組一旦初始化,其長度是不可變的。

//創建數組及初始化代碼
double[] myList = new double[10];
myList[0] = 5.6;
myList[1] = 4.5;
myList[2] = 3.3;
myList[3] = 13.2;
myList[4] = 4.0;
myList[5] = 34.33;
myList[6] = 34.0;
myList[7] = 45.45;
myList[8] = 99.993;
myList[9] = 11123;

  下圖展示了這個數組

3、數組大小和默認值

  當數組分配空間時,必須指定該數組能夠存儲的元素個數,從而確定數組大小。創建數組之后就不能在修改它的大小。可以使用arrayName.length得到數組的大小。例如:prices.length為5.

  當創建數組后,它的元素被賦予默認值,數值型基本數據類型默認值為0,char型的默認值為'\u0000‘,boolean型的默認值為false,引用數據類型默認值為null.

4、訪問數組元素

  使用“數組名[下標]”的方式,數組下標從0開始,到arrayName.length - 1結束。

  數組中的每個元素都可以使用下面的語法表示,稱為下標變量(indexed variable)

  arrayName[index];(數組引用變量[下標];)

  例如:prices[4]表示數組prices的最后一個元素。

  創建數組后,下標變量與正常變量的使用方法相同。例如:下面代碼將prices[0]和prices[1]的值相加賦給prices[2].

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

  下面的循環是將0付給prices[0],1賦給prices[1]....4賦給prices[4];

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

5、處理數組

  處理數組元素時,經常會用到for循環,理由有以下兩點:

  1).數組中的所有元素都是同一類型的,可以使用循環以同樣的方式反復處理這些元素。
  2).由於數組的大小是已知的,所以很自然地使用for循環。
  假如創建如下數組
  double[] list = new double[10];

  下面是一些處理數組的例子:
  1)使用輸入值初始化數組:循環使用用戶輸入的數值初始化數組。

/**
 * 使用輸入值初始化數組:循環使用用戶輸入的數值初始化數組。
 */
public static void inputInitArray() {
  //聲明創建了大小為10的double型數組
  double[] list = new double[10];
  Scanner input = new Scanner(System.in);
  System.out.println("請輸入" + list.length + "個值:");
  for(int i = 0; i < list.length; i++)
    list[i] = input.nextDouble();
  input.close();
  printArray(list);
  }    

  2)使用隨機數初始化數組

/**
 * 使用隨機數初始化數組
 */
public static void randomInitArray() {
  double[] list = new double[10];
  //Math.random()---->[0,1)
  for(int i = 0; i < list.length; i++)
    list[i] = Math.random() * 100;
    printArray(list);
}

  3)顯示數組,打印數組中的每一個元素

/**
* 打印數組中的每個元素
* @param arr double型數組
*/
public static void printArray(double[] arr) {
  //第一種方式:使用for循環
  System.out.print("[");
  for(int i = 0; i < arr.length; i++)
    System.out.print(arr[i] + (i == arr.length - 1 ? "]\r\n" : ", "));
    //第二種形式:使用Arrays工具類完成打印
    System.out.println("=========================");
    System.out.println(Arrays.toString(arr));
}

  4)對所有元素進行求和,使用名為total的變量存儲和。total的值初始化為0.

	/**
	 * 對double數組中所有元素進行求和
	 * @param arr double數組
	 * @return 和
	 */
	public static double sum(double[] arr) {
		double total  = 0;
		for(int i = 0; i < arr.length; i++)
			total += arr[i];
		return total;
	}

  5)找出最大值
  使用名為max的變量存儲最大值。將max的值初始化為list[0],為了找出數組list中的最大元素,將每個元素與max比較,如果該元素大於max,則更新max

	/**
	 * 獲取double數組中最大元素
	 * @param arr double數組
	 * @return double數組中最大元素
	 */
	public static double getMax(double[] arr) {
		double max = arr[0];
		for(int i = 1; i < arr.length; i++) {
			if(max < arr[i]) max = arr[i];
		}
		return max;
	}

  6) 找出最大元素的最小下標值

	/**
	 * 找出最大元素的最小下標值(如果數組中含有多個最大元素,那么找出最大元素第一次出現的位置)
	 * @param arr double數組
	 * @return double數組中最大元素的最小索引
	 */
	public static int getMaxElementIndex(double[] arr) {
		double max = arr[0];
		int index = 0;
		for(int i = 1; i < arr.length; i++) {
			if(max < arr[i]) {
				max = arr[i];
				index = i;
			}
		}
		return index;
	}

  7)隨機打亂,在很多應用中,需要對數組中的元素進行任意的重新排序(如洗牌),這稱作打亂(shuffling)。為了完成這種功能,針對每個元素list[i],隨意產生一個下標j,然后將list[i]和list[j]互換 .

            

/**
* 隨機打亂:對數組中的元素進行任意的重新排序,
* 針對每個元素arr[i],隨意產生一個下標j,然后將arr[i]和arr[j]進行互換
* @param arr 要打亂的數組
*/
public static void shuffing(double[] arr) {
  for(int i = 0; i < arr.length; i++) {
    int j  =(int) (Math.random() * (i + 1));
    double temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }
  System.out.println(Arrays.toString(arr));
}

  8)移動元素:向左或者向右移動元素,這里的例子就是將元素向左移動一個位置:

/**
 * 移動元素:向左或者向右移動元素,這里的例子就是將數組中每個元素依次向左移動一個位置
 * 將第一個元素值放在最后一個元素
 * @param arr 
 */
 public static void moveElement(double[] arr) {
  double temp = arr[0];
  for(int i = 1; i < arr.length; i++) {
    arr[i - 1] = arr[i];
  }
  arr[arr.length - 1] = temp;
}

  9)簡化編碼:對於某些任務來說,數組可以極大簡化編碼,比如前面的生肖年。

String[] years = {"猴","雞","狗","豬","鼠","牛","虎","兔","龍","蛇","馬","羊"};
System.out.println(year + "年的生肖為:" + years[year % 12]);

6、foreach循環
  java支持一個簡便的for循環,稱為foreach循環,即不使用下標變量就可以順序地遍歷整個數組:

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

  可以讀作:對list中每個元素e進行以下操作。注意,變量e必須聲明為與list中元素相同的數據類型。

  通常,foreach的語法為:

  for(elementType element : arrayName){

    //Process the element

  }
  但是 ,當需要使用其它順序遍歷數組或改變數組中的元素時,還是必須使用下標變量

  警告:越界訪問數組時經常會出現的程序設計錯誤,會拋出一個運行錯誤ArrayIndexOutOfBoundsException。為了避免錯誤的發生,在使用時應該確保所使用的下標不超過數組.length - 1.
數組的索引下標是從0開始的(下標為0表示第一個元素)

7、示例:分析數字
  回到開篇中讀取100個數,計算它們的平均值,然后找出有多少個數大於平均值。為了更加靈活地處理任意數目的輸入,我們讓用戶給出輸入的個數,而不是將其固定為100.

import java.util.Scanner;

public class AnalyzeNumber {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
      
        System.out.println("請輸入要存儲的個數");
        int n = input.nextInt();
        
        double[] numbers = new double[n];
        double sum = 0;
        
        System.out.println("請輸入您想存儲的數字");
        for(int i = 0;i < numbers.length;i++){
            numbers[i] = input.nextDouble();
            sum += numbers[i];
        }
        
        double avg = sum / n;
        System.out.println("平均值為:"+avg);
        
        for (int i = 0; i < n; i++) {
            if(numbers[i] > avg){
                System.out.println(numbers[i]);
            }
        }
        input.close();
    }
}

8:數組的復制(*)

  要將一個數組中的內容復制到另外一個數組中,你需要將數組的每一個元素復制到另外一個數組中。

  在程序中經常需要復制一個數組或數組的一部分。在這種情況下,你可能會嘗試使用賦值語句(=),如下所示:

    list2 = list1;

  該語句並不能將list1引用的數組內容復制給list'2,而只是將list1的引用值復制給了list2.在這條語句后,list1和list2都指向同一個數組,如下圖所示。list2原先引用的數組不能再引用,它就變成了垃圾,會被java虛擬機自動收回(垃圾回收)。

  在Java中,可以使用復制語句復制基本數據類型的變量,但不能復制數組。將一個數組變量賦值給另一個數組變量,實際上是將一個數組的引用賦值給另一個變量,使兩個變量都指向相同的內存地址。

  復制數組有三種方法:

  • 使用循環語句逐個地復制數組的元素。
int[] sourceArray = {2,5,8,10,200,-20};
int[] targetArray = new int[sourceArray.length];
for(int i = 0; i < sourceArray.length; i++)
    targetArray[i] = sourceArray[i];
  • 使用System類中的靜態方法arraycopy。
System.arraycopy(sourceArray,srcPos,targetArray,tarPos,length);
其中,參數srcPos和tarPos分別表示在源數組sourceArray和目標數組targetArray中的起始位置,從sourceArray復制到targetArray中的元素個數由參數length指定。例如:
System.arraycopy(sourceArray,0,targetArray,0,sourceArray.length);
  • 使用clone方法復制數組。

示例:一副牌
  從一副52張的牌中隨機挑出4張牌。所有的牌可以用一個名為deck的數組表示,這個數組從0到51的初始值來填充:

int[] deck = new int[52];
//初始化牌
for(int i = 0; i < deck.length; i++)
    deck[i] = i;

  牌號從0到12、13到25、26到38以及39到51分別表示13張黑桃、13張紅桃,13張方塊、13張梅花,cardNumber / 13決定牌的花色,而cardNumber % 13 決定是具體花色中的哪張牌。在洗牌(打亂數組deck)后,從deck中選出前4張牌

                         

                                                   52張牌存儲在一個名為deck的數組中

                      

                                                               CardNumber標識一張牌的花色和等級數字

/**
 * 從一副52張的牌中隨機挑出4張牌
 * @author Adan
 *
 */
public class DeckDemo {
    public static void main(String[] args) {
        //聲明一個52個元素空間大小的int數組來表示52張牌
        int[] deck = new int[52];
        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;
        }
        //定義一個代表撲克牌花色的數組
        String[] suits = {"♠","♥","♣","♦"};
        //同理,定義牌號的數組
        String[] ranks = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
        for(int i = 0; i < 3;i++) {
            String suit = suits[deck[i] / 13];//取得花色
            String rank = ranks[deck[i] % 13];
            System.out.println(suit + " " + rank);
        }
    }
}

將數組傳遞給方法

   當一個數組傳遞給方法時,數組的引用被傳遞給方法

public static double getMax(double[] arr) {
    double max = arr[0];
    for(int i = 1; i < arr.length; i++) {
        if(max < arr[i]) max = arr[i];
    }
    return max;
 }

  Java使用按值傳遞(pass-by-value)的方式將實參傳遞給方法。傳遞基本數據類型變量的值與傳遞數組值有很大的不同。

  • 對於基本數據類型參數,傳遞的是實參的值。
  • 對於數組類型參數,參數值是數組的引用,給方法傳遞的這個引用。從語義上來講,最好的描述就是參數傳遞的是共享信息(pass-by-sharing),既方法中的數組和傳遞的數組是一樣的。所以,如果改變方法中的數組,將會看到方法外的數組也變化了。 

從方法中返回數組

/**
     * 從方法返回一個數組(反轉后的數組)
     * @param list 源數組
     * @return 反轉后的數組
     */
    public static int[] reverse(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;
    }

示例:統計每個字母出現的次數

   隨機生成100個小寫字母並將其放入到一個字符數組中,對該數組中每個字母出現的次數進行統計。

public class StatisticsLetterCount {
	/**
	 * 創建一個大小為100的字符數組,用於存放隨機生成的小寫字母
	 * @return 字符數組
	 */
	public static char[] createLetters() {
		char[] letters = new char[100];
		for(int i = 0; i < letters.length; i++) {
			letters[i] = generateRandomLowerLetter();
		}
		return letters;
	}

	public static char generateRandomLowerLetter() {
		return (char)('a' + Math.random() * ('z' - 'a'));
	}
	
	public static void showLetterCount(char[] letters) {
		int[] times = new int[26];//每個元素初始值為0,times[0]代表a出現的次數,times[25】代表z出現的次數
		for(int i = 0; i < letters.length; i++) 
			times[letters[i] - 'a']++;
		for(int i = 0; i < times.length; i++) 
			System.out.print((char)('a' + i) + ":" + times[i] + ((i + 1) % 10 == 0 ? "\r\n" : "\t"));
	}
	
	public static void main(String[] args) {
		showLetterCount(createLetters());
	}
}

可變長參數列表

   具有相同類型的可變長參數可以傳遞給方法,可變參數的本質為數組

public static int sum(int ... nums) {
	if(nums == null) return 0;
	int sum = 0;
	for(int num : nums) sum+= num;
	return sum;
}

  注意:一個方法只能有一個可變參數且只能出現在最后位置。

數組的查找

  • 線性查找
     /**
       * 線性查找關鍵字元素key在數組中的索引
	 * 從數組的第一個元素依次的比較,如果該元素==關鍵字元素,返回該元素對應的索引。
	 * @param key 關鍵字
	 * @param list 數組
	 * @return 如果查找到該元素,就返回該元素對應的索引,沒有找到返回-1
	 */
	public static int linearSearch(int key,int[] list) {
		for(int i = 0; i < list.length; i++) {
			if(list[i] == key) return i;
		}
		return -1;
	}
  • 二分查找
     /**
	 * 折半查找
	 * 		前提:數組是一個有序數組
	 * 每次折半取中間元素
	 * 1.如果關鍵字小於中間元素,只需要在數組的前一半進行查找
	 * 2.如果關鍵字大於中間元素,只需要在數組的后一半進行查找
	 * 3.相等,就返回mid
	 */
	public static int binarySearch(int key, int[] list) {
		int low = 0;
		int high = list.length - 1;
		while(high >= low) {
			System.out.println("-----");
			int mid = (low + high) >>> 1;
			if(key < list[mid])
				high = mid - 1;
			else if(key > list[mid])
				low = mid + 1;
			else
				return mid;
		}
		return -low - 1;
	}

數組的排序

  • 選擇排序
package edu.uestc.avatar;

import java.util.Arrays;

public class ArraySort {
    /**
     * 使用選擇排序將數組中的元素進行升序排列
     *     首先找到數組中最小的數,然后將它和第一個元素進行交換
     *     在剩下的數中找到最小數,然后將它和第二個元素進行交換
     *     依次類推,直到數列中只剩下一個元素
     * @param array 要排序的整數數組
     */
    public static void selectionSort(int[] array) {
        for(int i = 0; i < array.length - 1; i++) {
            int currentMin = array[i];
            int currentMinIndex = i;
            for(int j = i + 1; j < array.length; j++) {
                if(currentMin > array[j]) {
                    currentMin = array[j];
                    currentMinIndex = j;
                }
            }
            if(currentMinIndex != i) {
                array[currentMinIndex] = array[i];
                array[i] = currentMin;
            }
        }
    }
    
    public static void main(String[] args) {
        int[] list = {40, 60, 75, 72, 61, 73, 18, 35, 95, 70};
        selectionSort(list);
        System.out.println(Arrays.toString(list));
    }
}
  • 冒泡排序

Arrays類(java.util.Arrays)

  1.Arrays類包含一些實用的方法用於常見的數組操作,比如排序和查找

  2.Arrays類包含各種各樣的靜態方法,用於實現數組的排序和查找、數組的比較和填充數組元素,以及返回數組的字符串表示。這些方法都有對所有基本類型的重載方法。

  3.可以使用sort或parallelSort方法對整個數組或部分數組進行排序。

  4.可以采用二分查找法(binarySearch方法)在數組中查找關鍵字。數組必須提前按升序排列好。如果數組中不存在關鍵字,方法返回 -(插入點下標+1)。

  5.可以采用equals方法檢測兩個數組是否相等。如果他們的內容相同,那么這兩個數組相等。

  6.可以使用fill方法填充整個數組或部分數組。

  7.可以是同toString方法來返回一個字符串,該字符串中代表了數組中的所有元素。這是一個顯示數組中所有元素的快捷和簡便的方法。

命令行參數

 

課后作業(練習)

1.有一個數組:8,4,2,1,23,344,12

  1)循環輸出數組的值——使用三種方式。

  2)求數組中所有數值的和。

2.猜數游戲:從鍵盤中任意輸入一個數據,判斷數組中是否包含此數,要求每一個功能獨立成方法。

3.找出兩個數組中的差異元素並存入一個新的數組並排序,假設每個數組內部都沒有重復元素。

4.取1-100的10個隨機數存入數組中,要求數據不能重復

5. 設計一個方法,將數組中的數據打亂順序

6.n(自定義輸入)個人圍成一圈,並依次編號1-n,從編號為1的人開始按順時針方向每隔一人選出一個,剩下的人重新圍成一圈,如此循環,直到只剩下1,2人,如果你想成為這兩個幸運兒,問:最開始你應該站在什么位置.

7.給定一個整數數組,例如{6,4,7,2,5,8}和一個數字,例如10,請設計一個函數找出兩個元素(或同一個元素加自身),並且使這兩個數的和為給定數字,並打印出來(提示:先進行數組排序)

8.給定一個含有n個元素的整型數組a例如{1,1,1,2,4,3,3} ,如果某些元素出現的次數為奇數次,則將其輸出:例如1,2,4

9.冒泡排序(下沉排序)

10.打印兩個有序數組中的共同元素

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM