Arrays类
1.功能概述
- Arrays类是一个工具类,其中包含了数组操作的很多方法,比如搜索和排序
- Arrays类中的方法均为static修饰的,可以直接通过Arrays.xxx(xxx)的形式调用方法
2.几个重要方法
asList
由给定的数组a,返回一个固定大小的List对象。在这里,着重解释一下前面这句话的深层含义,我们可以看Arrays类的源码,来帮助我们理解
生成的List对象,是由所给的数组a来决定的,我们看一下源码实现
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
而这个ArrayList并不是java.util中的ArrayList类,而是Arrays的内部类ArrayList,源码为:
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return a.clone();
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
@Override
public E get(int index) {
return a[index];
}
@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
}
具体思路
public static <T> List<T> asList(T... a) {
//传入a这个数组,然后拿着这个数组直接构造
return new Arrays.ArrayList<>(a);
}
private static class ArrayList<E> extends AbstractList<E>
//这个内部类有一个数组类型的a
private final E[] a;
//内部ArrayList的构造,先非空判断之后给a赋值
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
具体操作示例
@Test
public void test1(){
int[] arr = {1,2,3};
List<Object> objects = Arrays.asList(arr);
System.out.println("arr = " + arr);//arr = [I@1b0375b3
System.out.println("objects = " + objects);//objects = [[I@1b0375b3],可以看到,又包了一层[]
}
所以,可以看出,最后生成的List实例的元素与数组a中的元素是一样的,并且,其长度和数组a的元素一样。
现在解释一下“固定长度的意思”:
List与数组的一个区别是,List的长度是可变的,可以对List进行插入和删除元素,数组的长度是固定的,而且不能从数组中删除元素,只能修改元素的值。利用Arrays.asList(array)将返回一个List,然而这个返回的List并不支持add和remove的操作。
那为什么不支持add和remove操作呢?只能上源码了:
我们在AbstractList中找到依据,如何实现插入和删除元素:
public boolean add(E e) {
add(size(), e);
return true;
}
//增加元素会直接抛异常,就是不让你添加的意思
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
//删除元素的时候也是会这样
public E remove(int index) {
throw new UnsupportedOperationException();
}
实验一下啊
小tips:当改变原数组中的元素时,会导致list对象中的相应元素发生改变;同样的,当生成的list对象中的元素做修改时,也会导致原来数组中相应的元素发生改变
fill
给定特定值val,使整个数组中或者某下标范围内的元素值为val
源码:
public static void fill(int[] a, int val) {
//从0-a的最大长度开始遍历,然后把每个下班都赋值val(传进来的参数)
for (int i = 0, len = a.length; i < len; i++)
a[i] = val;
}
//这个是有起始位置和结束位置的fill
public static void fill(int[] a, int fromIndex, int toIndex, int val) {
//先对这个起始位置和结束位置进行一个判断,有错误的话直接抛异常
rangeCheck(a.length, fromIndex, toIndex);
//赋值,如果没有赋值的还是默认值(0)
for (int i = fromIndex; i < toIndex; i++)
a[i] = val;
}
测试一下
@Test
public void test2(){
int[] arr1 = new int[7];
int[] arr2 = new int[7];
//把arr1里面的元素全部赋值为6
Arrays.fill(arr1,3);
//把arr2里面的元素从第三到第五赋值为6,其余的值还是初始值0
Arrays.fill(arr2,3,5,6);
for (int i : arr1) {
System.out.print(i+" ");//3 3 3 3 3 3 3
}
System.out.println();
for (int i : arr2) {
System.out.print(i+" ");//0 0 0 6 6 0 0
}
}
copyOf()
将原始数组的元素,复制到新的数组中,可以设置复制的长度(即需要被复制的元素个数)
先看源码
public static int[] copyOf(int[] original, int newLength) {
//先弄一个新的数组
int[] copy = new int[newLength];
//调用了System的拷贝数组的方法(native),从0把原数组拷贝到新数组
System.arraycopy(original, 0, copy, 0,
//如果原数组的长度<=新数组的长度,那就用原来的
//如果不是,那就用新的
Math.min(original.length, newLength));
return copy;
}
copyOfRange():将某个范围内的元素复制到新的数组中
public static int[] copyOfRange(int[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
int[] copy = new int[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
测试一下
@Test
public void test3() {
int[] arr_a = {1, 2, 3, 4, 5};
int[] arr_b;
int[] arr_c;
int[] arr_d;
//给定的长度大于源数组的长度,那么多的位置就用0来进行填充
arr_b = Arrays.copyOf(arr_a, 10);
for (int i : arr_b) {
System.out.print(i + " ");//1 2 3 4 5 0 0 0 0 0
}
System.out.println();
//给的长度比较小的话,那就小咯
arr_c = Arrays.copyOf(arr_a, 2);
for (int i : arr_c) {
System.out.print(i + " ");//1 2
}
System.out.println();
arr_d = Arrays.copyOfRange(arr_a, 2, 4);
for (int i : arr_d) {
System.out.print(i + " ");//3 4
}
}
equals()
判断两个数组中的元素是否一一对应相等
以int数组来分析,其他类型的数组原理一样
先看源码
public static boolean equals(int[] a, int[] a2) {
//如果a和a2的内存引用地址相同,那么肯定相同返回true
if (a==a2)
return true;
//如果a或者a2为null,那么肯定不行啊,返回false
if (a==null || a2==null)
return false;
//判断两个数组的长度是否相同,不相同肯定不一样啊,返回false
int length = a.length;
if (a2.length != length)
return false;
//循环遍历两个数组,挨个比对各个元素是否相同,只要有一个不相同,直接returnfalse
for (int i=0; i<length; i++)
if (a[i] != a2[i])
return false;
return true;
}
测试一下
@Test
public void test4() {
int[] a = new int[]{1, 2, 3};
int[] b = null;
int[] c = new int[]{};
int[] d = new int[]{1, 2, 3};
System.out.println(Arrays.equals(a, b));//false
System.out.println(Arrays.equals(a, c));//false
System.out.println(Arrays.equals(a, d));//true
System.out.println(Arrays.equals(b, c));//false
System.out.println(Arrays.equals(b, d));//false
System.out.println(Arrays.equals(c, d));//false
}
sort
对数组进行升序排序,排序后 ,数组中存放的是排序后的结果
以int数组来分析,其他类型的数组原理一样
源码分析
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
然后再点进去发现···
static void sort(int[] a, int left, int right,
int[] work, int workBase, int workLen) {
// Use Quicksort on small arrays
if (right - left < QUICKSORT_THRESHOLD) {
sort(a, left, right, true);
return;
}
/*
* Index run[i] is the start of i-th run
* (ascending or descending sequence).
*/
int[] run = new int[MAX_RUN_COUNT + 1];
int count = 0; run[0] = left;
// Check if the array is nearly sorted
for (int k = left; k < right; run[count] = k) {
if (a[k] < a[k + 1]) { // ascending
while (++k <= right && a[k - 1] <= a[k]);
} else if (a[k] > a[k + 1]) { // descending
while (++k <= right && a[k - 1] >= a[k]);
for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
}
} else { // equal
for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {
if (--m == 0) {
sort(a, left, right, true);
return;
}
}
}
/*
* The array is not highly structured,
* use Quicksort instead of merge sort.
*/
if (++count == MAX_RUN_COUNT) {
sort(a, left, right, true);
return;
}
}
// Check special cases
// Implementation note: variable "right" is increased by 1.
if (run[count] == right++) { // The last run contains one element
run[++count] = right;
} else if (count == 1) { // The array is already sorted
return;
}
// Determine alternation base for merge
byte odd = 0;
for (int n = 1; (n <<= 1) < count; odd ^= 1);
// Use or create temporary array b for merging
int[] b; // temp array; alternates with a
int ao, bo; // array offsets from 'left'
int blen = right - left; // space needed for b
if (work == null || workLen < blen || workBase + blen > work.length) {
work = new int[blen];
workBase = 0;
}
if (odd == 0) {
System.arraycopy(a, left, work, workBase, blen);
b = a;
bo = 0;
a = work;
ao = workBase - left;
} else {
b = work;
ao = 0;
bo = workBase - left;
}
// Merging
for (int last; count > 1; count = last) {
for (int k = (last = 0) + 2; k <= count; k += 2) {
int hi = run[k], mi = run[k - 1];
for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
b[i + bo] = a[p++ + ao];
} else {
b[i + bo] = a[q++ + ao];
}
}
run[++last] = hi;
}
if ((count & 1) != 0) {
for (int i = right, lo = run[count - 1]; --i >= lo;
b[i + bo] = a[i + ao]
);
run[++last] = right;
}
int[] t = a; a = b; b = t;
int o = ao; ao = bo; bo = o;
}
}
我看不懂了啊!
好吧,直接开始测试一下
@Test
public void test5() {
int[] a = new int[]{22, 1, 4, 65, 3, 55, 10};
Arrays.sort(a);
for (int i : a) {
System.out.print(i + " ");//1 3 4 10 22 55 65
}
}
binarySearch
对排序好的数组,采用二分查找的方式查找某个元素,可以在整个数组中查找,也可以在某个范围内查找。
以int数组来分析,其他类型的数组原理一样
源码
private static int binarySearch0(int[] a, int fromIndex, int toIndex,
int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
测试一下
@Test
public void test6(){
int[] a = {1,45,78,23,123,98,67,12,90,56};
//返回索引
int i = Arrays.binarySearch(a, 123);
System.out.println("i = " + i);//4
//找不到的话就返回-5
int i1 = Arrays.binarySearch(a, 111);
System.out.println("i1 = " + i1);//-5
//给一个区间范围让他去找
int i2 = Arrays.binarySearch(a, 1, 2, 222);
}
toString
就是把数组转换成字符串输出
先看源码
public static String toString(int[] a) {
//如果为空,直接返回空的字符串表达形式
if (a == null)
return "null";
//设置一个iMax为a的长度-1
int iMax = a.length - 1;
//如果-1之后=负一,那么说明原来的数组长度为0,也就是啥都没得,直接返回一个[]
if (iMax == -1)
return "[]";
//这里用的是StringBuilder来进行拼接操作
StringBuilder b = new StringBuilder();
//先拼接一个开头[
b.append('[');
//开始一个循环操作
for (int i = 0; ; i++) {
//开始往StringBuilder里面去拼接值
b.append(a[i]);
//如果索引=数组的长度-1,也就是说到头了
if (i == iMax)
//那么就拼一个结束]
return b.append(']').toString();
//如果没结束,那就拼一个,
b.append(", ");
}
}
测试一下
@Test
public void test7(){
int[] arr = {2, 34, 35, 4, 657, 8, 69, 9};
//输出的是地址值
System.out.println(arr);//[I@1b0375b3
//将int类型的数组转换成字符串表现格式
Arrays.sort(arr);//升序排序
String string = Arrays.toString(arr);
System.out.println(string);//[2, 4, 8, 9, 34, 35, 69, 657]
}