Java数组中的实用API(附带源码分析)

这一小节比较简单,就是一些简单的数组API的调用。

复制数组

我们需要先知道将会用到的两个方法:

public static void fill(int[] a, int val) {
        for (int i = 0, len = a.length; i < len; i++)
            a[i] = val;
    }

上面的代码是JavaSE中的标准代码,第一个参数是想要添加元素的数组,第二个是选择的值。
第二个是:

public static native void arraycopy(Object src,  int  srcPos,
Object dest, int destPos, int length);

这是Sytem类中的源代码,注意这个方法的修饰词native,这里表示这个方法是调用了底层的接口,并不是java的原生实现。下面是方法实现:

import java.util.*;
import static net.mindview.util.Print.*;

public class CopyingArrays
{
    public static void main(String[] args){
        int[] i = new int[7];
        Integer[] i2 = new Integer[7];
        Integer[] i3 = new Integer[7];
        int[] i4 = new int[7];
        Arrays.fill(i, new Integer(8));
        Arrays.fill(i2, 7);
        Arrays.fill(i3, new Integer(9));
        print(Arrays.toString(i));
        //输出:[8, 8, 8, 8, 8, 8, 8]
        print(Arrays.toString(i2));
        //输出:[7, 7, 7, 7, 7, 7, 7]
        print(Arrays.toString(i4));
        //输出:[0, 0, 0, 0, 0, 0, 0]
        System.arraycopy(i, 2, i4, 2, 3);
        print(Arrays.toString(i4));
        //输出:[0, 0, 8, 8, 8, 0, 0]
        System.arraycopy(i2, 2, i3, 2, 3);
        print(Arrays.toString(i3));
        //输出:[9, 9, 7, 7, 7, 9, 9]
    }
}
Arrays.fill(i, new Integer(8));

上面的i是一个int[]数组,但是Arrays.fill()可以通过自动包装机制把Integer类型的值加入int[]型的数组。
但是:

Integer[] i3 = new int[7];

上面的代码将报错,因为不能对数组使用自动包装机制。
对于System.arraycopy()方法不能使用自动包装机制

System.arraycopy(i, 0, i2, 0, i.length);

i是int[],i2是Integer[],这个地方没有办法解释为什么不行,因为这里System.arraycopy()是native修饰词的方法,原因和底层有关,以后有机会再补充。

数组的比较

这个比较简单,先说方法,然后看看源码:

    Arrays.equals(a1, a2);

a1,a2是将要进行比较的数组,下面是源代码:

public static boolean equals(int[] a, int[] a2) {
    if (a==a2)//如果两个数组引用的是相同的地址,那么两者肯定相同
        return true;
    if (a==null || a2==null)//两者任一引用为空则不可能相同
        return false;

    int length = a.length;
    if (a2.length != length)//如果两者长度不同则不相同
        return false;

    for (int i=0; iif (a[i] != a2[i])
            return false;

    return true;
}

下面是方法的用法实例:

import java.util.*;
import static net.mindview.util.Print.*;

public class ComparingArrays
{
    public static void main(String[] args){
        int[] a1 = null;
        int[] a2 = null;
        Arrays.fill(a1, 7);
        Arrays.fill(a2, 7);
        print(Arrays.equals(a1, a2));
        //输出:true
    }
}

当然也可以用于对象数组,这里不再赘述。

数组元素的排序

Java有一个通用的接口用于比较——Comparable,Comparable的源码如下:

public interface Comparable<T> {
    public int compareTo(T o);
}

Comparable接口需要我们实现一个CompareTo方法。这个方法可以配合Arrays.sort()方法进行使用:

import java.util.Arrays;
class SortArr implements Comparable
{
    public int id;
    public SortArr(int id){
        this.id = id;
    }
    public int compareTo(SortArr other)
    {
        return id > other.id ? 1:(id == other.id ? 0 : -1);
    }
    public String toString()
    //这个地方我覆盖了父类的toString()方法,方便在Arrays.toString()中使用
    {
        return(id +"");
    }
}
public class Try
{
    public static void main(String[] args)
    {
        SortArr[] arr = new SortArr[5];
        for(int i=5; i>0; i--){
            arr[Math.abs((i-5)%5)] = new SortArr(i);
        }
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

下面是输出:

[5, 4, 3, 2, 1]
[1, 2, 3, 4, 5]

当然我们还有另外一个接口:Comparator,它的源码复杂且很长,下次我会专门写一篇博文讲解他的源码,我么现在只用知道我们如果想要调用这个接口,我们必须实现两个方法:

int compare(T o1, T o2);
boolean equals(Object obj);

下面是它的具体用法:

import java.util.*;

class SortArr implements Comparator
{
    public int id;
    SortArr(){}
    SortArr(int id){
        this.id = id;
    }
    public int compare(SortArr first, SortArr second){
        return first.id > second.id ? 1 : (first.id == second.id ? 0 : -1);
    }
    //这里很奇怪,虽然这个接口需要我们自己实现equals方法,但是实际上我们不用实现
    public String toString(){
        return this.id+"";
    }
}
public class Try
{
    public static void main(String[] args)
    {
        SortArr[] arr = new SortArr[5];
        for(int i=5; i>0; i--){
            arr[Math.abs((i-5)%5)] = new SortArr(i);
        }
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr,new SortArr());
        System.out.println(Arrays.toString(arr));
    }
}

下面是输出:

[5, 4, 3, 2, 1]
[1, 2, 3, 4, 5]

可以看到Comparable和Comparator方法都可以很好的和Arrays.sort()方法进行合作,只不过Comparable接口我们只需要直接在
Arrays.sort(Array)放入需要排序的数组,但是Comparator我们还需要在Arrays.sort(Array,Comparator)放入我们实现了Comparator接口的类。
JavaSE中有一些默认的规则,和一些已经定义好的Comparator,举个例子:

Arrays.sort(String[]);

上面是默认按照字典序排序,所以大写字母在前,小写字母在后。
有一些默认的Iterator:

Collections.reverseOrder();

会按照逆序进行排序。

String.CASE_INSENSITIVE_ORDER;

将会无视大小写进行排序。
Java标准类库针对不同的类型的排序算法进行了优化,基本类型是快速排序,对象则是稳定归并排序。

在数组中查找

Arrays类有一个静态方法Arrays.binarySearch(),使用这个静态方法的前提是必须先对数组进行排序,不是递增的数组调用这个静态方法,举个例子:

int[] arr = new int[5];
for(int i=0; i<5; i++){
    arr[Math.abs(i-4)] = i;
}
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.binarySearch(arr, 7));
System.out.println(Arrays.binarySearch(arr, 2));

输出为:

[4, 3, 2, 1, 0]
-6
2

规则就是如果存在在数组中,那么返回这个值的索引位置,如果不在,那么会返回应该插入的位置,值为-(插入位置)-1;如果大于整个数组中的最大数,那么插入位置等于arr.size()。
Arrays.binarySearch(T[], key,Comparator)是一个重载方法,可以把比较器传入。具体用法如下:

String[] arr = new String[4];
arr[0] = "b";
arr[1] = "c";
arr[2] = "a";
arr[3] = "d";
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.binarySearch(arr,"c",Collections.reverseOrder()));
System.out.println(Arrays.toString(arr));

输出为:

[b, c, a, d]
1
[b, c, a, d]

可以看到这个方法我们是不会改变arr本来的顺序,也可以进行搜索,返回本来的位置。
但是我们自己去定义类和实现Comparator方法就没有这么幸运了:

SortArr[] arr = new SortArr[5];
for(int i=5; i>0; i--){
    arr[Math.abs((i-5)%5)] = new SortArr(i);
}
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.binarySearch(arr, arr[1], new SortArr()));
System.out.println(Arrays.toString(arr));

输出为:

[5, 4, 3, 2, 1]
-6
[5, 4, 3, 2, 1]

可以看到这个时候我们的自定义的并不能很好的运行,我们还是需要先排序才行。

为什么不要用数组

我觉得数组的操作很多都并没有封装好,程序员的思维不应该是任何代码都亲历亲为而是应该最快速的开发最优的代码,所以我们应该使用封装好的容器,而不是完全空白的、基本上没有封装好的方法的数组,下面是两种将数组转化为List的方法
1.

Integer[] arr = new Integer[5];
for(int i=0; i<5; i++){
    arr[i] = i;
}
List list = Arrays.asList(arr);
System.out.println(list.toString());

输出:

[0, 1, 2, 3, 4]

我们应该注意,int[]和Integer[]之间并不能互相转换,但是容器都是带了参数类型的,所以我们并不能很好的将基本类型的数组转化为容器。
2.

Integer[] arr = new Integer[5];
for(int i=0; i<5; i++){
    arr[i] = i;
}
List list = new ArrayList();
Collections.addAll(list, arr);
System.out.println(list.toString());

输出:

[0, 1, 2, 3, 4]

你可能感兴趣的:(Java,SE)