Java基础 -- 数组

一  初识数组

一、数组的概念

若是说如今要求你定义100个整型变量,按照以往的方式,定义100个整型变量:html

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

可是这个时候若是按照此类方式定义就会很是麻烦,由于这些变量彼此之间没有任何的关联,也就是说若是如今忽然再有一个要求,要求你输出这100个变量的内容,意味着你要编写System.out.println()语句100次。java

而Java 语言中提供的数组能够用来存储固定大小的同类型元素。数组中的元素能够是任何数据类型,包括基本数据类型和引用数据类型。数组

要使用Java的数组,必须通过两个步骤:安全

  • 声明数组变量;
  • 建立数组:分配内存给这个数组;

二、声明数组

首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:dom

元素类型[] 数组名;  // 建议使用的方式
元素类型 数组名[];  // 效果相同,这种风格相似 C/C++ 语言 

数组名”是用来统一这组相同数据类型的元素的名称,建议使用有意义的名称为数组命名。函数

咱们声明一个数组,并赋值为null,以下:性能

int[] score = null;  // 推荐方式,null表示引用数据类型的默认值 // int socre[] = null; // 非推荐方式

该语句会在栈内存中建立一个变量score,以下图:测试

三、为数组开辟空间

数组声明后其实是在栈内存中保存了此数组的名称,接下来即是要在堆内存中分配数组所需的内存,其中“元素的个数”(数组的长度)是告诉编译器,所声明的数组要存放多少个元素,而“new”则是命令编译器根据括号里的长度开辟空间。this

数组名 = new 元素类型[元素的个数];

咱们为上面声明的socre数组开辟空间:spa

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区别小结

相关文章
相关标签/搜索