Java基礎 -- 數組

一  初識數組

一、數組的概念

若是說如今要求你定義100個整型變量,按照以往的方式,定義100個整型變量:html

int i1, i2, i3, ... i100;

可是這個時候若是按照此類方式定義就會很是麻煩,由於這些變量彼此之間沒有任何的關聯,也就是說若是如今忽然再有一個要求,要求你輸出這100個變量的內容,意味着你要編寫System.out.println()語句100次。java

而Java 語言中提供的數組能夠用來存儲固定大小的同類型元素。數組中的元素能夠是任何數據類型,包括基本數據類型和引用數據類型。數組

要使用Java的數組,必須通過兩個步驟:安全

  • 聲明數組變量;
  • 建立數組:分配內存給這個數組;

二、聲明數組

首先必須聲明數組變量,才能在程序中使用數組。下面是聲明數組變量的語法:dom

元素類型[] 數組名;  // 建議使用的方式
元素類型 數組名[];  // 效果相同,這種風格相似 C/C++ 語言 

數組名」是用來統一這組相同數據類型的元素的名稱,建議使用有意義的名稱爲數組命名。函數

咱們聲明一個數組,並賦值爲null,以下:post

    int[] score = null;  // 推薦方式,null表示引用數據類型的默認值
    // int socre[] = null;  // 非推薦方式

該語句會在棧內存中建立一個變量score,以下圖:性能

三、爲數組開闢空間

數組聲明後其實是在棧內存中保存了此數組的名稱,接下來即是要在堆內存中分配數組所需的內存,其中「元素的個數」(數組的長度)是告訴編譯器,所聲明的數組要存放多少個元素,而「new」則是命令編譯器根據括號裏的長度開闢空間。測試

數組名 = new 元素類型[元素的個數];

咱們爲上面聲明的socre數組開闢空間:this

score = new int[3];

數組操做中,在棧內存中保存的永遠是數組的名稱,只開闢了棧內存空間數組是永遠沒法使用的,必須有指向的堆內存纔可使用,要想開闢新的堆內存則必須使用new關鍵字,以後只是將此堆內存的使用權交給了對應的棧內存空間,並且一個堆內存空間能夠同時被多個棧內存空間指向,即:一我的能夠有多個名字,人就至關於堆內存,名字就至關於棧內存。

四、數組的動態初始化

咱們能夠在聲明數組的同時分配內存:

元素類型 數組名[] = new 元素類型[元素的個數];

下面咱們聲明一個元素個數爲10的整型數組score,同時開闢一塊內存空間供其使用:

int[] score = new int[10];

在Java中,因爲整數數據類型所佔用的空間爲4個bytes,而整型數組score可保存的元素有10個,因此上例中佔用的內存共有4 * 10 = 40個字節。

數組是引用類型,它的元素至關於類的成員變量,所以數組一經分配空間,其中的每一個元素也被按照成員變量一樣的方式被隱式初始化。

public class ArrayTest {
    public static void main(String[] args)
    {
        //聲明並開闢空間
        String [] stringArray = new String[3];    //各元素的值默認爲String類型的初始值null
        
        for(int i=0;i<stringArray.length;i++)
        {
            System.out.println(stringArray[i]);
            
        }                
    }
}

輸出以下:

null
null
null

五、數組中元素的表示方法

想要訪問數組裏的元素,能夠利用索引來完成。Java的數組索引編號由0開始,以一個的score[10]的整形數組爲例,score[0]表明第1個元素,score[1]表明第2個元素,score[9]爲數組中第10個元素(也就是最後一個元素)。

程序中能夠發現,對於數組的訪問採用「數組名稱[index]的方式,score一共開闢了10個空間大小的數組,因此下標的取值是0~9,假設程序中取出的內容超過了這個下標,如score[10],則程序運行的時候會出現數組下標越界的錯誤提示:java.lang.ArrayIndexOutOfBoundsException。

爲數組中的元素賦值並進行輸出:

public static void Test1() {
        
        //[1] 聲明數組,但未開闢堆內存
        int[] score = null;
        
      //[2] 爲數組開闢堆內存空間,大小爲3
        score = new int[3];
        
     // [3] 爲每個元素賦值
        for (int x = 0; x < score.length; x++) { 
            score[x] = x * 2 + 1 ; 
        } 
        
     // 使用循環依次輸出數組中的所有內容
        for (int x = 0; x < 3; x++) { 
            System.out.println("score[" + x + "] = " + score[x]);
        }
    }

執行該函數,輸出以下:

score[0] = 1
score[1] = 3
score[2] = 5

咱們對以上代碼進行內存分析,【1】【2】【3】處分別對應下面這三張圖:

六、數組的靜態初始化

數組的內容分爲動態初始化和靜態初始化兩種,前面所講解的所有代碼是採用先聲明數組以後爲數組中的每一個內容賦值的方式完成的。那麼也能夠經過數組靜態初始化在數組聲明時就爲數組元素分配空間並賦值。

元素類型 數組名 = {元素1,元素2...};
元素類型 數組名 = new 元素類型[]{元素1,元素2...};

七、遍歷處理

數組的元素類型和數組的大小都是肯定的,因此當處理數組元素時候,咱們一般使用基本循環,尤爲是for循環。JDK 1.5 引進了一種新的循環類型,被稱爲 foreach 循環或者增強型循環,它能在不使用下標的狀況下遍歷數組。

      public static void Test2() {
           
          double[] arr = {1.5, 2.5, 3.5, 3.5, 5.5};
          
          // 打印全部數組元素
          
          for (double element: arr) {
             System.out.println(element);
          }
       }

八、多維數組

 多維數組能夠當作是數組的數組,好比二維數組就是一個特殊的一維數組,其每個元素都是一個一維數組(每一個一維數組均可以具備任意的長度)。咱們以二維數組爲例介紹多維數組的初始化。

(1)二維數組的動態初始化,直接爲每一維分配空間:

元素類型 變量名 = new 元素類型[行數][列數];

聲明整型數組score,同時爲其開闢一塊內存空間:

int[][] score = new int[4][3];

整型數據score可保存的元素有4*3 = 12個,而在Java中,int數據類型所佔用的空間爲4個字節,所以該整型數組佔用的內存共爲4*12 = 48個字節。

      public static void Test3() {
          // 聲明並實例化二維數組
          int score[][] = new int[4][3];
          
          // 爲數組中的部份內容賦值
          score[0][1] = 30 ; 
          score[1][0] = 31 ; 
          score[2][2] = 32 ; 
          score[3][1] = 33 ; 
          score[1][1] = 30 ; 
          
          for (int i = 0; i < score.length; i++) { 
              for(int j=0;j<score[i].length;j++){
                  System.out.print(score[i][j] + "\t");
              }
              // 換行
              System.out.println("") ; 
            }    
      }

輸出結果以下:

0    30    0    
31    30    0    
0    0    32    
0    33    0    

(2)二維數組的靜態初始化:

元素類型 變量名 = {{元素A1,元素A2...}, {元素B1,元素B2}...};

例如:

int score[][] = {{ 67, 61 }, { 78, 89, 83 }, { 99, 100, 98, 66, 95 }};

 通常來說,操做二維數組不該使用常數來控制維數。具體方法是array.length表示行數,array[row].length來表示row行的列數。這樣當數組行數和列數不相等時,代碼能夠自動調整爲正確的值。

      public static void Test4() {
          //靜態初始化一個二維數組,每行的數組元素個數不同
          int[][] score = {{ 67, 61 }, { 78, 89, 83 }, { 99, 100, 98, 66, 95 }};
          
          for (int i = 0; i < score.length; i++) {
              for (int j = 0; j < score[i].length; j++) {
                  System.out.print(score[i][j] + "\t");
              }
              // 換行
              System.out.println(""); 
          }
          
          System.out.println(Arrays.toString(score));
          System.out.println(Arrays.deepToString(score));
      }

輸出以下:

67    61    
78    89    83    
99    100    98    66    95    
[[I@15db9742, [I@6d06d69c, [I@7852e922]
[[67, 61], [78, 89, 83], [99, 100, 98, 66, 95]]

有一點須要注意,若是想將多維數組轉換爲多個String,正如從輸出中所看到的那樣,咱們須要使用Arrays.deepToString()方法。

二 數組進階

一、返回一個數組

有時候,咱們在寫一個方法,咱們但願它的返回不止一個值,而是一組值。這對於C和C++這樣的語言來講有點困難,由於它們不能返回一個數組,而只能返回指向數組的指針。這回形成一些問題,由於它使得控制數組的聲明週期變得很困難,而且容易形成內存泄露。

在Java中,咱們能夠直接返回一個數組,當咱們使用完這個數組後,垃圾回收器會清理掉它。

下面演示如何返回String型數組:

import java.util.*;

public class IceCream {
    private static Random rand = new Random(47);
    static final String[] FLAVORS = {
            "Chocolate","Strawberry","Vanilla Fudge Swirl",
            "Mint Chip","Mocha Almond Fudge","Rum Raisin",
            "Praline Crean","Mud Pie"
    };
    
    
    public static String[] flavorSet(int n) {
        if(n > FLAVORS.length)
            throw new IllegalArgumentException("Set too big");
        
        String [] results = new String[n];
        boolean [] picked = new boolean[FLAVORS.length];
        
        for(int i=0;i<n;i++) {
            int t;
            do {
                t = rand.nextInt(FLAVORS.length);
            }while(picked[t]);
            
            results[i] = FLAVORS[t];
            picked[i] = true;            
        }
        return results;        
    }
    
    public static void main(String[] args) {
        for(int i=0;i<7;i++) {
            System.out.println(Arrays.toString(flavorSet(3)));
        }
    }

}

輸出以下:

[Rum Raisin, Mint Chip, Mocha Almond Fudge]
[Chocolate, Strawberry, Mocha Almond Fudge]
[Strawberry, Mint Chip, Mocha Almond Fudge]
[Rum Raisin, Vanilla Fudge Swirl, Mud Pie]
[Vanilla Fudge Swirl, Mocha Almond Fudge, Praline Crean]
[Strawberry, Praline Crean, Mocha Almond Fudge]
[Mocha Almond Fudge, Strawberry, Mocha Almond Fudge]

二、數組和泛型

一般,數組和泛型不能很好的結合,你不能實例化一個泛型數組:

List<String>[] ls = new List<String>[10]; 

因爲泛型的擦除特性,會擦除參數類型信息,而數組必須知道它們所持有的確切類型,以強制保證類型安全

編譯器雖然不容許你實例化泛型數組,可是,它容許你建立這種數組的引用。例如:

List<String>[] ls ;

這條語句能夠順利的經過編譯器而不報任何錯誤。並且,儘管你不能建立實際的持有泛型的數組對象,可是你能夠建立非泛型的輸入,而後將其轉型:

import  java.util.*;


class BerylliumSphere{
    private static long counter;
    private final long id = counter++;
    public String toString() {
        return "Sphere " + id;
    }
    
}

public class ArrayOfGenerics {
    public static void main(String[] args) {
            List<String> [] ls;            
            List[] la = new List[10];
            
            //"Unchecked" warning
            ls = (List<String>[])la;            
            ls[0] = new ArrayList<String>();
            //Compile-time checkng produces an error
            //ls[1] = new ArrayList<Integer>();
            
            //The problem: List<String> is a subtype of Object
            Object[] objects = ls;     //So assignment is Ok
            //Compiles and runs without complaint
            objects[1] = new ArrayList<Integer>();
            
            List<BerylliumSphere> [] spheres = (List<BerylliumSphere> [])new List[10];
            
            for(int i=0;i<spheres.length;i++) {
                spheres[i] = new ArrayList<BerylliumSphere>(); 
            }                                    
    }
}

一旦擁有了對List<String>[] 的引用,你就會看到某些編譯器檢查。問題是數組是協變類型的,所以List<String>[] 也是一個Object[],而且你能夠利用這一點,將一個ArrayList<Integer>賦值到你的數組中,而不會有任何編譯期或運行時錯誤。

若是你知道未來不會向上轉型,而且需求也相對比較簡單,那麼你仍然能夠建立泛型數組,它能夠提供基本的編譯期類型檢查。

通常而言,你會發現泛型在類或方法的邊界處頗有效,而在類或方法的內部,擦除一般使得泛型變得不適用。例如,你不能建立泛型數組:

class ArrayOfGenericsType<T>{
    T[] array;
    @SuppressWarnings("unchecked")
    public ArrayOfGenericsType(int size) {
        //array = new T[size];    //Illegal  
        array = (T[]) new  Object[size];        //"unckecked" Warning
    }    
}

擦除在這裏成爲了障礙——本例試圖建立的類型已被擦除,於是是類型位置的數組。可是咱們能夠建立Object數組,而後將其轉型,若是沒有@SuppressWarnings("unchecked")註解,你將在編譯期獲得一個」不受檢查「的錯誤消息,由於這個數組沒有真正持有或動態檢查類型T,也就是說,若是我建立一個String[],Java在編譯期和運行期都會強制要求我只能將String對象置於該數組中,可是,若是建立的是Object[],那麼我就能夠將除基本類型以外的任何對象置於該數組中。

三 建立測試數組

有時候在程序測試的時候,咱們須要快速生成一個數組,主要能夠採用如下方式。

一、Arrays.fill()

Java標準庫Arrays提供了一個fill()方法:針對值類型,用同一個值填充數組各個位置,而針對對象類型,就是複製同一個引用進行填充。下面是一個示例:

import java.util.*;
public class FillingArrays {
    public static void main(String[] args){
        int size = 6;
        boolean[] a1 = new boolean[size];
        byte[] a2 = new byte[size];
        char[] a3 = new char[size];
        short[] a4 = new short[size];
        int[] a5 = new int[size];
        long[] a6 = new long[size];
        float[] a7 = new float[size];
        double[] a8 = new double[size];
        String[] a9 = new String[size];
        
        Arrays.fill(a1, true);
        System.out.println("a1=" + Arrays.toString(a1));
        
        Arrays.fill(a2, (byte)11);
        System.out.println("a2=" + Arrays.toString(a2));
        
        
        Arrays.fill(a3, 'x');
        System.out.println("a3=" + Arrays.toString(a3));
        
        
        Arrays.fill(a4, (short)17);
        System.out.println("a4=" + Arrays.toString(a4));
        
        Arrays.fill(a5, 19);
        System.out.println("a5=" + Arrays.toString(a5));
        
        Arrays.fill(a6, 23);
        System.out.println("a6=" + Arrays.toString(a6));
        
        Arrays.fill(a7, 29);
        System.out.println("a7=" + Arrays.toString(a7));
        
        Arrays.fill(a8, 47);
        System.out.println("a8=" + Arrays.toString(a8));
        
        Arrays.fill(a9,"Hello");
        System.out.println("a9=" + Arrays.toString(a9));
        
        Arrays.fill(a9,3,5, "world");
        System.out.println("a9=" + Arrays.toString(a9));
        
    }
}

輸出以下:

a1=[true, true, true, true, true, true]
a2=[11, 11, 11, 11, 11, 11]
a3=[x, x, x, x, x, x]
a4=[17, 17, 17, 17, 17, 17]
a5=[19, 19, 19, 19, 19, 19]
a6=[23, 23, 23, 23, 23, 23]
a7=[29.0, 29.0, 29.0, 29.0, 29.0, 29.0]
a8=[47.0, 47.0, 47.0, 47.0, 47.0, 47.0]
a9=[Hello, Hello, Hello, Hello, Hello, Hello]
a9=[Hello, Hello, Hello, world, world, Hello]

使用Arrays.fill()能夠填充整個數組,或者像最後兩條語句所示,只填充數組的某個區域。

二、數據生成器

爲了以靈活的方式建立更有意義的數組,咱們可使用生成器。首先咱們來展現一個例子:

//定義Generator接口
interface Generator<T>{
    public T next();    
}

public class CountingGenerator {
    //Boolean類型生成器
    public static class Boolean implements Generator<java.lang.Boolean>{
        private boolean value = false;
        public java.lang.Boolean next(){
            //反轉
            value = !value;
            return value;
        }        
    };
    
    //Byte類型生成器
    public static class Byte implements Generator<java.lang.Byte>{
        private byte value = 0;
        public java.lang.Byte next(){                        
            return value++;
        }        
    };
    
    private static char[] chars = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
    
    //Character類型生成器
    public static class Character implements Generator<java.lang.Character>{
        private int index = -1;
        public java.lang.Character next(){
            index = (index + 1)%chars.length;
            return chars[index];
        }        
    };
    
    //String類型生成器
    public static class String implements Generator<java.lang.String>{
        private int length = 7;
        Generator<java.lang.Character> cg = new Character();
        public String() {}
        public String(int length) {this.length = length;}
        public java.lang.String next(){                        
            char[] buf = new char[length];
            for(int i=0;i<length;i++) {
                buf[i] = cg.next();
            }
            return new java.lang.String(buf);
        }        
    };
    
    
    //Short類型生成器
    public static class Short implements Generator<java.lang.Short>{
        private short value = 0;
        public java.lang.Short next(){            
            return value++;
        }        
    };
    
    //Integer類型生成器
    public static class Integer implements Generator<java.lang.Integer>{
        private int value = 0;
        public java.lang.Integer next(){            
            return value++;
        }        
    };
    
    
    //Long類型生成器
    public static class Long implements Generator<java.lang.Long>{
        private long value = 0;
        public java.lang.Long next(){            
            return value++;
        }        
    };
    
    
    //Float類型生成器
    public static class Float implements Generator<java.lang.Float>{
        private float value = 0;
        public java.lang.Float next(){            
            return value++;
        }        
    };
    
    //Double類型生成器
    public static class Double implements Generator<java.lang.Double>{
        private double value = 0;
        public java.lang.Double next(){            
            return value++;
        }        
    };

}

下面咱們經過反射來測試這個類:

//生成器測試
public class GeneratorsTest {
    public static int size = 10;
    
    public static void test(Class<?> surroundingClass) {        
        //獲取這個surroundingClass中的全部類的Class對象
        for(Class<?> type:surroundingClass.getClasses()) {
            System.out.print(type.getSimpleName()+":");
            
            try {
                Generator<?> g = (Generator<?>)type.newInstance();
                for(int i=0;i<size;i++) {
                    System.out.printf(g.next() + " ");
                }
                System.out.println();                
            }catch(Exception e) {
                throw new RuntimeException(e);
            }
        }                    
    }
    
    public static void main(String[] args) {
        test(CountingGenerator.class);
    }
}

輸出以下:

Boolean:true false true false true false true false true false 
Byte:0 1 2 3 4 5 6 7 8 9 
Character:a b c d e f g h i j 
Double:0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 
Float:0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 
Integer:0 1 2 3 4 5 6 7 8 9 
Long:0 1 2 3 4 5 6 7 8 9 
Short:0 1 2 3 4 5 6 7 8 9 
String:abcdefg hijklmn opqrstu vwxyzAB CDEFGHI JKLMNOP QRSTUVW XYZabcd efghijk lmnopqr 

這裏假設待測試類包含一組嵌套的Generator實現類,其中每一個Generator都有一個默認構造器。因爲反射方法getClass()能夠獲取全部的嵌套類的Class對象。所以經過test()方法能夠爲Generator的每一種實現建立一個實例,而後打印經過調用10次next()方法而產生的結果。

下面的是一組使用隨機數生成器的Generator。由於Random的構造器使用常量進行初始化,因此,每次用這些Generator中的一個來運行程序時、所產生的輸出都是可重複的:

import java.util.*;

public class RandomGenerator {
    private static Random r = new Random(7);
    
    //Boolean類型生成器
        public static class Boolean implements Generator<java.lang.Boolean>{            
            public java.lang.Boolean next(){                
                return r.nextBoolean();
            }        
        };
        
        //Byte類型生成器
        public static class Byte implements Generator<java.lang.Byte>{            
            public java.lang.Byte next(){                        
                return (byte)r.nextInt();
            }        
        };
        
        private static char[] chars = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
        
        //Character類型生成器
        public static class Character implements Generator<java.lang.Character>{            
            public java.lang.Character next(){                
                return CountingGenerator.chars[r.nextInt(CountingGenerator.chars.length)];
            }        
        };
        
        //String類型生成器
        public static class String extends CountingGenerator.String{            
            {cg = new Character();}
            public String() {}
            public String(int length) {super(length);}        
        }
                                                
        
        //Short類型生成器
        public static class Short implements Generator<java.lang.Short>{            
            public java.lang.Short next(){            
                return (short)r.nextInt();
            }        
        };
        
        //Integer類型生成器
        public static class Integer implements Generator<java.lang.Integer>{
            private int mod = 1000;
            public Integer() {}
            public Integer(int modulo) {mod = modulo;}
            public java.lang.Integer next(){            
                return r.nextInt(mod);
            }        
        };
        
        
        //Long類型生成器
        public static class Long implements Generator<java.lang.Long>{
            private int mod = 1000;
            public Long() {}
            public Long(int modulo) {mod = modulo;}
            public java.lang.Long next(){            
                return  new java.lang.Long(r.nextInt(mod));
            }        
        };
        
        
        //Float類型生成器
        public static class Float implements Generator<java.lang.Float>{            
            public java.lang.Float next(){            
                int trimmed = Math.round(r.nextFloat()*100);
                return ((float)trimmed)/100;
            }        
        };
        
        //Double類型生成器
        public static class Double implements Generator<java.lang.Double>{            
            public java.lang.Double next(){            
                long trimmed = Math.round(r.nextDouble()*100);
                return ((double)trimmed)/100;
            }        
        };
}

其中,RandomGenerator.String繼承自CountingGenerator.String,而且只是插入了新的Character生成器。

爲了避免生成過大的數字, RandomGenerator.Integer默認使用的模數爲10000,可是重載的構造器容許選擇更小的值。一樣的方式也應用到RandomGenerator.Long上。對於Float和Double生成器,小數點以後的數字被裁掉了。

咱們複用GeneratorTest來測試RandomGenerator:

public class RandomGeneratorTest {
    public static void main(String[] args) {
        GeneratorsTest.test(RandomGenerator.class);
    }

}

輸出以下:

Boolean:true true true false false false true true true true 
Byte:0 -95 -23 -33 -65 -4 2 -27 22 -50 
Character:k q J B o i d S C V 
Double:0.38 0.77 0.22 0.31 0.87 0.1 0.44 0.52 0.1 0.6 
Float:0.75 0.0 0.39 0.77 0.83 0.86 0.46 0.77 0.45 0.45 
Integer:674 525 896 829 272 305 820 105 529 347 
Long:454 652 637 720 114 396 980 221 31 598 
Short:26209 24236 16342 21060 -3366 -26047 3920 -7806 7282 -4356 
String:oAhjszO BzFCoFO BplkgxC nvsQPpA xrtFbBn fCvhWSf DSryYmD YneWpFx EOOqDuQ TLGdVlb 

 四 Arrays實用功能

Arrays類位於 java.util 包中,主要包含了操縱數組的各類方法:

一、Arrays.asList(T… data) 

該方法返回的是Arrays內部靜態類ArrayList,而不是咱們日常使用的ArrayList,,該靜態類ArrayList沒有覆蓋父類的add, remove等方法,因此若是直接調用,會報UnsupportedOperationException異常。

將數組轉換爲集合,接收一個可變參:

List<Integer> list = Arrays.asList(1, 2, 3);
list.forEach(System.out::println); // 1 2 3
Integer[] data = {1, 2, 3};
List<Integer> list = Arrays.asList(data);
list.forEach(System.out::println); // 1 2 3

若是將基本數據類型的數組做爲參數傳入, 該方法會把整個數組看成返回的List中的第一個元素

int[] data = {1, 2, 3};
List<int[]> list = Arrays.asList(data);
System.out.println(list.size()); // 1
System.out.println(Arrays.toString(list.get(0))); // [1, 2, 3]

二、Arrays.fill()

Arrays.fill(Object[] array, Object obj):用指定元素填充整個數組(會替換掉數組中原來的元素)

Integer[] data = {1, 2, 3, 4};
Arrays.fill(data, 9);
System.out.println(Arrays.toString(data)); // [9, 9, 9, 9]

Arrays.fill(Object[] array, int fromIndex, int toIndex, Object obj):用指定元素填充數組,從起始位置到結束位置,取頭不取尾(會替換掉數組中原來的元素)

Integer[] data = {1, 2, 3, 4};
Arrays.fill(data, 0, 2, 9);
System.out.println(Arrays.toString(data)); // [9, 9, 3, 4]

三、Arrays.sort()

Arrays.sort(Object[] array):對數組元素進行排序(串行排序)

String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
Arrays.sort(data);
System.out.println(Arrays.toString(data)); // [1, 2, 3, 4]

Arrays.sort(T[] array, Comparator<? super T> comparator):使用自定義比較器,對數組元素進行排序(串行排序)

String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
// 實現降序排序,返回-1放左邊,1放右邊,0保持不變
Arrays.sort(data, (str1, str2) -> {
    if (str1.compareTo(str2) > 0) {
        return -1;
    } else {
        return 1;
    }
});
System.out.println(Arrays.toString(data)); // [4, 3, 2, 1]

Arrays.sort(Object[] array, int fromIndex, int toIndex):對數組元素的指定範圍進行排序(串行排序)

String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
// 對下標[0, 3)的元素進行排序,即對1,4,3進行排序,2保持不變
Arrays.sort(data, 0, 3);
System.out.println(Arrays.toString(data)); // [1, 3, 4, 2]

Arrays.sort(T[] array, int fromIndex, int toIndex, Comparator<? super T> c):使用自定義比較器,對數組元素的指定範圍 進行排序(串行排序)

String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
// 對下標[0, 3)的元素進行降序排序,即對1,4,3進行降序排序,2保持不變
Arrays.sort(data, 0, 3, (str1, str2) -> {
    if (str1.compareTo(str2) > 0) {
        return -1;
    } else {
        return 1;
    }
});
System.out.println(Arrays.toString(data)); // [4, 3, 1, 2]

四、Arrays.parallelSort() 

其重載方法與 sort() 相同
Arrays.parallelSort(T[] array):對數組元素進行排序(並行排序),當數據規模較大時,會有更好的性能:

String[] data = {"1", "4", "3", "2"};
Arrays.parallelSort(data);
System.out.println(Arrays.toString(data)); // [1, 2, 3, 4]

五、Arrays.binarySearch() 

注意:在調用該方法以前,必須先調用sort()方法進行排序,若是數組沒有排序, 那麼結果是不肯定的,此外若是數組中包含多個指定元素,則沒法保證將找到哪一個元素。

Arrays.binarySearch(Object[] array, Object key):使用二分法查找數組內指定元素的索引值:

搜索元素是數組元素,返回該元素索引值:

Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
System.out.println(Arrays.binarySearch(data, 1)); // 0

搜索元素不是數組元素,且小於數組中的最小值:

Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
// 此時程序會把數組看做 {0, 1, 3, 5, 7},此時0的索引值爲0,則搜索0時返回 -(0 + 1) = -1
System.out.println(Arrays.binarySearch(data, 0)); // -1

搜索元素不是數組元素,且大於數組中的最小值:

Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
// 此時程序會把數組看做 {1, 2, 3, 5, 7},此時2的索引值爲1,則搜索2時返回 -(1 + 1) = -2
System.out.println(Arrays.binarySearch(data, 2)); // -2

總結:binarySearch()方法若是找到了目標,方法的返回值等於或大於0.不然,它產生負返回值,表示若要保持數組的排序狀態此目標元素所應該插入的位置。這個負值的計算公式是:-(插入點+1),「插入點」是指,第一個大於查找的元素在數組中的位置,若是數組中全部的元素都小於要查找的元素,「插入點」就等於數組的長度。此外,若是使用Comparator排序了某個對象數組,在使用Arrays.binarySearch()時必須提供一樣的Comparator。

Arrays.binarySearch(Object[] array, int fromIndex, int toIndex, Object obj):使用二分法查找數組內指定範圍內的指定元素的索引值:

Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
// {1, 3},3的索引值爲1
System.out.println(Arrays.binarySearch(data, 0, 2, 3)); // 1

六、Arrays.copyOf()

Arrays.copyOf(T[] original, int newLength):拷貝數組,其內部調用了System.arraycopy()方法,從下標0開始,若是超過原數組長度,會用null進行填充:

Integer[] data1 = {1, 2, 3, 4};
Integer[] data2 = Arrays.copyOf(data1, 2);
System.out.println(Arrays.toString(data2)); // [1, 2]
Integer[] data2 = Arrays.copyOf(data1, 5);
System.out.println(Arrays.toString(data2)); // [1, 2, 3, 4, null]

這裏咱們來介紹一下System.arraycopy()方法,它是Java標準類庫中的static方法,用它複製數組比用for循環複製要快不少。System.arraycopy()方法針對全部類型作了重載,下面的例子是用來處理int數組的:

import java.util.*;
public class CopyingArrays {
    public static void main(String[] args) {
        int[] i = new int[7];
        int[] j = new int[10];
        Arrays.fill(i, 47);
        Arrays.fill(j, 99);
        System.out.println("i="+Arrays.toString(i));
        System.out.println("j="+Arrays.toString(j));
        
        System.arraycopy(i, 0, j, 0, i.length);
        System.out.println("j="+Arrays.toString(j));
    }
}

輸出以下:

i=[47, 47, 47, 47, 47, 47, 47]
j=[99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
j=[47, 47, 47, 47, 47, 47, 47, 99, 99, 99]

arraycopy()須要的參數有:源數組,表示從源數組中的什麼位置開始複製的偏移量;目標數組,表示從目標數組的什麼位置開始複製的偏移量,以及須要複製的元素個數。

注意:arraycopy()不會執行自動包裝和自動拆包,兩個數組必須具備相同的確切類型。

七、Arrays.copyOfRange(T[] original, int from, int to)

拷貝數組,指定起始位置和結束位置,若是超過原數組長度,會用null進行填充:

Integer[] data1 = {1, 2, 3, 4};
Integer[] data2 = Arrays.copyOfRange(data1, 0, 2);
System.out.println(Arrays.toString(data2)); // [1, 2]
Integer[] data2 = Arrays.copyOfRange(data1, 0, 5);
System.out.println(Arrays.toString(data2)); // [1, 2, 3, 4, null]

八、Arrays.equals(Object[] array1, Object[] array2)

判斷兩個數組是否相等,實際上比較的是兩個數組的哈希值,即 Arrays.hashCode(data1) == Arrays.hashCode(data2):

Integer[] data1 = {1, 2, 3};
Integer[] data2 = {1, 2, 3};
System.out.println(Arrays.equals(data1, data2)); // true

九、Arrays.deepEquals(Object[] array1, Object[] array2)

判斷兩個多維數組是否相等,實際上比較的是兩個數組的哈希值,即 Arrays.hashCode(data1) == Arrays.hashCode(data2):

Integer[][] data1 = {{1,2,3}, {1,2,3}};
Integer[][] data2 = {{1,2,3}, {1,2,3}};
System.out.println(Arrays.deepEquals(data1, data2)); // true

十、Arrays.hashCode(Object[] array)

返回數組的哈希值:

Integer[] data = {1, 2, 3};
System.out.println(Arrays.hashCode(data)); // 30817

十一、Arrays.deepHashCode(Object[] array)

返回多維數組的哈希值:

Integer[][] data = {{1, 2, 3}, {1, 2, 3}};
System.out.println(Arrays.deepHashCode(data)); // 987105

十二、Arrays.toString(Object[] array)

返回數組元素的字符串形式:

Integer[] data = {1, 2, 3};
System.out.println(Arrays.toString(data)); // [1, 2, 3]

1三、Arrays.deepToString(Object[] array)

返回多維數組元素的字符串形式:

Integer[][] data = {{1, 2, 3}, {1, 2, 3}};
System.out.println(Arrays.deepToString(data)); // [[1, 2, 3], [1, 2, 3]]

1四、Arrays.spliterator(T[] array)

返回數組的分片迭代器,用於並行遍歷數組:

public class Students {

    private String name;

    private Integer age;

    public Students(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    // 省略get、set方法
}

public static void main(String[] args) {
    Students[] data = new Students[5];
    IntStream.range(0,5).forEach(i -> data[i] = new Students("小明"+i+"號", i));
    // 返回分片迭代器
    Spliterator<Students> spliterator = Arrays.spliterator(data);
    spliterator.forEachRemaining(stu -> {
        System.out.println("學生姓名: " + stu.getName() + "  " + "學生年齡: " + stu.getAge());
        // 學生姓名: 小明0號  學生年齡: 0
        // 學生姓名: 小明1號  學生年齡: 1
        // 學生姓名: 小明2號  學生年齡: 2
        // 學生姓名: 小明3號  學生年齡: 3
        // 學生姓名: 小明4號  學生年齡: 4
    });
}

1五、Arrays.stream(T[] array)

返回數組的流Stream,而後咱們就可使用Stream相關的許多方法了:

Integer[] data = {1, 2, 3, 4};
List<Integer> list = Arrays.stream(data).collect(toList());
System.out.println(list); // [1, 2, 3, 4]

參考文章

[1]Java數組

[2]Java-Arrays類經常使用方法詳解

[3]Java中Comparable和Comparator區別小結

相關文章
相關標籤/搜索