# (31) 剖析Arrays / 计算机程序的思维逻辑

toString

Arrays的toString方法可以方便的输出一个数组的字符串形式，方便查看，它有九个重载的方法，包括八种基本类型数组和一个对象类型数组，这里列举两个：

```public static String toString(int[] a)

public static String toString(Object[] a)
```

```int[] arr = {9,8,3,4};
System.out.println(Arrays.toString(arr));

String[] strArr = {"hello", "world"};
System.out.println(Arrays.toString(strArr));
```

```[9, 8, 3, 4]
[hello, world]
```

```int[] arr = {9,8,3,4};
System.out.println(arr);

String[] strArr = {"hello", "world"};
System.out.println(strArr);
```

```[I@1224b90
[Ljava.lang.String;@728edb84
```

```public static void sort(int[] a)
public static void sort(double[] a)
```

```int[] arr = {4, 9, 3, 6, 10};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
```

```[3, 4, 6, 9, 10]
```

sort还可以接受两个参数，对指定范围内的元素进行排序，如：

`public static void sort(int[] a, int fromIndex, int toIndex)`

```int[] arr = {4, 9, 3, 6, 10};
Arrays.sort(arr,0,3);
System.out.println(Arrays.toString(arr));
```

```[3, 4, 9, 6, 10]
```

```public static void sort(Object[] a)
public static void sort(Object[] a, int fromIndex, int toIndex)
```

```String[] arr = {"hello","world", "Break","abc"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
```

```[Break, abc, hello, world]
```

"Break"之所以排在最前面，是因为大写字母比小写字母都小。那如果排序的时候希望忽略大小写呢？

sort还有另外两个重载方法，可以接受一个比较器作为参数：

```public static <T> void sort(T[] a, Comparator<? super T> c)
public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c)
```

```public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
```

String类有一个public静态成员，表示忽略大小写的比较器：

```public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
```

```String[] arr = {"hello","world", "Break","abc"};
Arrays.sort(arr, String.CASE_INSENSITIVE_ORDER);
System.out.println(Arrays.toString(arr));
```

```[abc, Break, hello, world]
```

```private static class CaseInsensitiveComparator
implements Comparator<String> {
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
}```

sort默认都是从小到大排序，如果希望按照从大到小排呢？对于对象类型，可以指定一个不同的Comparator，可以用匿名内部类来实现Comparator，比如可以这样：

```String[] arr = {"hello","world", "Break","abc"};
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareToIgnoreCase(o1);
}
});
System.out.println(Arrays.toString(arr));
```

```[world, hello, Break, abc]
```

Collections类中有两个静态方法，可以返回逆序的Comparator，如

```public static <T> Comparator<T> reverseOrder()

public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)```

```String[] arr = {"hello","world", "Break","abc"};
Arrays.sort(arr, Collections.reverseOrder(String.CASE_INSENSITIVE_ORDER));
System.out.println(Arrays.toString(arr));
```

Arrays包含很多与sort对应的查找方法，可以在已排序的数组中进行二分查找，所谓二分查找就是从中间开始找，如果小于中间元素，则在前半部分找，否则在后半部分找，每比较一次，要么找到，要么将查找范围缩小一半，所以查找效率非常高。

```public static int binarySearch(int[] a, int key)
public static int binarySearch(int[] a, int fromIndex, int toIndex,
int key)```

`public static int binarySearch(Object[] a, Object key)`

```public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
```

```int[] arr = {3,5,7,13,21};
System.out.println(Arrays.binarySearch(arr, 13));```

```int[] arr = {3,5,7,13,21};
System.out.println(Arrays.binarySearch(arr, 11));
```

```public static long[] copyOf(long[] original, int newLength)
public static <T> T[] copyOf(T[] original, int newLength)```

newLength表示新数组的长度，如果大于原数组，则后面的元素值设为默认值。回顾一下默认值，对于数值类型，值为0，对于boolean，值为false，对于char，值为'\0'，对于对象，值为null。

```String[] arr = {"hello", "world"};
String[] newArr = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(newArr));```

```[hello, world, null]
```

`public static int[] copyOfRange(int[] original, int from, int to)`

from表示要拷贝的第一个元素的索引，新数组的长度为to-from，to可以大于原数组的长度，如果大于，与copyOf类似，多出的位置设为默认值。

```int[] arr = {0,1,3,5,7,13,19};
int[] subArr1 = Arrays.copyOfRange(arr,2,5);
int[] subArr2 = Arrays.copyOfRange(arr,5,10);
System.out.println(Arrays.toString(subArr1));
System.out.println(Arrays.toString(subArr2));```

```[3, 5, 7]
[13, 19, 0, 0, 0]
```

subArr1是正常的子数组，subArr2拷贝时to大于原数组长度，后面的值设为了0。

```public static boolean equals(boolean[] a, boolean[] a2)

public static boolean equals(double[] a, double[] a2)
public static boolean equals(Object[] a, Object[] a2)```

Arrays包含很多fill方法，可以给数组中的每个元素设置一个相同的值：

```public static void fill(int[] a, int val)
```

```public static void fill(int[] a, int fromIndex, int toIndex, int val)
```

```int[] arr = {3,5,7,13,21};
Arrays.fill(arr,2,4,0);
System.out.println(Arrays.toString(arr));
```

```[3, 5, 0, 0, 21]
```

```public static int hashCode(int a[])
```

```public static int hashCode(int a[]) {
if (a == null)
return 0;

int result = 1;
for (int element : a)
result = 31 * result + element;

return result;
}```

```int[][] arr = new int[2][3];
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
arr[i][j] = i+j;
}
}```

arr就是一个二维数组，第一维长度为2，第二维长度为3，类似于一个长方形矩阵，或者类似于一个表格，第一维表示行，第二维表示列。arr[i]表示第i行，它本身还是一个数组，arr[i][j]表示第i行中的第j个元素。

`int[][][] arr = new int[10][10][10];`

```int[][] arr = new int[2][];
arr[0] = new int[3];
arr[1] = new int[5];
```

arr是一个二维数组，第一维的长度为2，第一个元素的第二维长度为3，而第二个为5。

Arrays中的toString，equals，hashCode都有对应的针对多维数组的方法：

```public static String deepToString(Object[] a)
public static boolean deepEquals(Object[] a1, Object[] a2)

public static int deepHashCode(Object a[])
```

```int[][] arr = new int[][]{
{0,1},
{2,3,4},
{5,6,7,8}
};
System.out.println(Arrays.deepToString(arr));
```

```[[0, 1], [2, 3, 4], [5, 6, 7, 8]]
```

hashCode的实现我们已经介绍了，fill和equals的实现都很简单，循环操作而已，就不赘述了。

toString

toString的实现也很简单，利用了StringBuilder，我们列下代码，但不做解释了。

```public static String toString(int[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";

StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
```

copyOf和copyOfRange利用了 System.arraycopy，逻辑也很简单，我们也只是简单列下代码：

```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;
}
```

```private static <T> int binarySearch0(T[] a, int fromIndex, int toIndex,
T key, Comparator<? super T> c) {
int low = fromIndex;
int high = toIndex - 1;

while (low <= high) {
int mid = (low + high) >>> 1;
T midVal = a[mid];
int cmp = c.compare(midVal, key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
}```

Apache有一个开源包(http://commons.apache.org/proper/commons-lang/)，里面有一个类ArrayUtils (位于包org.apache.commons.lang3)，里面实现了更多的常用数组操作，这里列举一些，与Arrays类似，每个操作都有很多重载方法，我们只列举一个。

```public static void reverse(final int[] array)
```

```//从头往后找

public static int indexOf(final int[] array, final int valueToFind)

//从尾部往前找

public static int lastIndexOf(final int[] array, final int valueToFind)

//检查是否包含元素

public static boolean contains(final int[] array, final int valueToFind)
```

```//删除指定位置的元素

public static int[] remove(final int[] array, final int index)
//删除多个指定位置的元素

public static int[] removeAll(final int[] array, final int... indices)

//删除值为element的元素，只删除第一个

public static boolean[] removeElement(final boolean[] array, final boolean element)
```

```//添加一个元素

public static int[] add(final int[] array, final int element)

//在指定位置添加一个元素

public static int[] add(final int[] array, final int index, final int element)
//合并两个数组

public static int[] addAll(final int[] array1, final int... array2)
```

```public static boolean isSorted(int[] array)
```

0 条评论

• ### (27) 剖析包装类 (中) / 计算机程序的思维逻辑

本节继续探讨包装类，主要介绍Integer类，下节介绍Character类，Long与Integer类似，就不再单独介绍了，其他类基本已经介绍完了，不再赘述。 ...

• ### (70) 原子变量和CAS / 计算机程序的思维逻辑

从本节开始，我们探讨Java并发工具包java.util.concurrent中的内容，本节先介绍最基本的原子变量及其背后的原理和思维。 原子变量 什么是原子...

• ### 计算机程序的思维逻辑 (3) - 基本运算

运算 上节我们介绍了给数据赋值，有了初始值之后，可以对数据进行运算。计算机之所以称为"计算"机，是因为发明它的主要目的就是运算。运算有不同的类型，不同的数据类型...

• ### P2279 [HNOI2003]消防局的设立

题目描述 2020年，人类在火星上建立了一个庞大的基地群，总共有n个基地。起初为了节约材料，人类只修建了n-1条道路来连接这些基地，并且每两个基地都能够通过道路...

• ### View的工作原理

View的绘制流程是从ViewRoot的PerformTraversals方法开始的。它经过measure，layout，draw三个过程将view绘制出来。m...

• ### 洛谷P4383 [八省联考2018]林克卡特树lct(DP凸优化/wqs二分)

小L 最近沉迷于塞尔达传说：荒野之息（The Legend of Zelda: Breath of The Wild）无法自拔，他尤其喜欢游戏中的迷你挑战。

• ### Good Bye 2018 B. New Year and the Treasure Geolocation(思维)

题目链接：http://codeforces.com/contest/1091/problem/B