Arrays类介绍
- Arrays类是一个工具类,其中包含了数组操作的很多方法,包括搜索和排序
Arrays常用的几种方法
1、asList()
由给定的一个数组,返回一个固定大小的list对象
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
{
@java.io.Serial
private static final long serialVersionUID = -2764017481108945198L;
@SuppressWarnings("serial") // Conditionally serializable
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return Arrays.copyOf(a, a.length, Object[].class);
}
@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) >= 0;
}
@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);
}
@Override
public Iterator<E> iterator() {
return new ArrayItr<>(a);
}
}
我们可以看到这一个ArrayList是没有add和remove方法的,这就是前面我们说到的asList()生成的是一个固定长度的List
如果使用AbstractList中的add和remove的方法,会抛出异常
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
说明很重要的一点
Arrays.asList(T…a)生成的List对象的大小是由原来给定的数组a决定的
2、fill()
给定特定值val,使整个数组中或者某下标范围内的元素值为val
由于fill()重载的方法有很多,所以我们选择最多使用的int类型来看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-niJfNzOT-1620986008092)(C:\Users\86710\AppData\Roaming\Typora\typora-user-images\image-20210514141341475.png)]
public static void fill(int[] a, int val) {
for (int i = 0, len = a.length; i < len; i++)
a[i] = val;
}
public static void fill(int[] a, int fromIndex, int toIndex, int val) {
rangeCheck(a.length, fromIndex, toIndex);
for (int i = fromIndex; i < toIndex; i++)
a[i] = val;
}
这个源码实现上非常简单,这里不做解释
3、copyOf()以及copyOfRange()
- copyOf():将原始数组的元素复制到新的数组中,可以设置复制的长度(即需要被复制的长度)
- copyOfRange():将某个范围内的元素复制到新的数组中。
copyOf()源码:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
这是一个泛型的方法,如果这个newType是继承了Object[]数组类型的,即直接new Object[]数组,否则使用Array.newInstance方法创建,之后使用System.arraycopy()方法将原始数组的元素复制过去新的数组。
copyOfRange()源码:
@HotSpotIntrinsicCandidate
public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
与copyOf()源码一样,仅仅是加了一个newlength的计算和判断是否合法
4、equals()
判断两个数组中的元素是否一一对应相等
这里只贴上int数组的原理,其他类型都差不多
public static boolean equals(int[] a, int[] a2) {
if (a==a2)
return true;
if (a==null || a2==null) //其中一个或两个都是null false
return false;
int length = a.length;
if (a2.length != length) //长度不等false
return false;
return ArraysSupport.mismatch(a, a2, length) < 0;
}
public static int mismatch(int[] a,
int[] b,
int length) {
int i = 0;
if (length > 1) {
if (a[0] != b[0]) //头部元素不同 结束
return 0;
i = vectorizedMismatch(
a, Unsafe.ARRAY_INT_BASE_OFFSET,
b, Unsafe.ARRAY_INT_BASE_OFFSET,
length, LOG2_ARRAY_INT_INDEX_SCALE);
if (i >= 0)
return i;
i = length - ~i;
}
for (; i < length; i++) {
if (a[i] != b[i])
return i;
}
return -1;
}
5、sort()最常用
对数组进行升序排序(从小到大)
同样只贴出int数组的排序
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, 0, a.length);
}
static void sort(int[] a, int parallelism, int low, int high) {
int size = high - low;
if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
int depth = getDepth(parallelism, size >> 12);
int[] b = depth == 0 ? null : new int[size];
new Sorter(null, a, b, low, size, low, depth).invoke();
} else {
sort(null, a, 0, low, high);
}
}
private static final int MIN_PARALLEL_SORT_SIZE = 4 << 10;
4 << 10是4096,当size小于这个值的时候就会走下面这一个sort
static void sort(Sorter sorter, int[] a, int bits, int low, int high) {
while (true) {
int end = high - 1, size = high - low;
/*
* Run mixed insertion sort on small non-leftmost parts.
*/
if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
mixedInsertionSort(a, low, high - 3 * ((size >> 5) << 3), high);
return;
}
/*
* Invoke insertion sort on small leftmost part.
*/
if (size < MAX_INSERTION_SORT_SIZE) {
insertionSort(a, low, high);
return;
}
/*
* Check if the whole array or large non-leftmost
* parts are nearly sorted and then merge runs.
*/
if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
&& tryMergeRuns(sorter, a, low, size)) {
return;
}
/*
* Switch to heap sort if execution
* time is becoming quadratic.
*/
if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
heapSort(a, low, high);
return;
}
/*
* Use an inexpensive approximation of the golden ratio
* to select five sample elements and determine pivots.
*/
int step = (size >> 3) * 3 + 3;
/*
* Five elements around (and including) the central element
* will be used for pivot selection as described below. The
* unequal choice of spacing these elements was empirically
* determined to work well on a wide variety of inputs.
*/
int e1 = low + step;
int e5 = end - step;
int e3 = (e1 + e5) >>> 1;
int e2 = (e1 + e3) >>> 1;
int e4 = (e3 + e5) >>> 1;
int a3 = a[e3];
/*
* Sort these elements in place by the combination
* of 4-element sorting network and insertion sort.
*
* 5 ------o-----------o------------
* | |
* 4 ------|-----o-----o-----o------
* | | |
* 2 ------o-----|-----o-----o------
* | |
* 1 ------------o-----o------------
*/
if (a[e5] < a[e2]) { int t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
if (a[e4] < a[e1]) { int t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
if (a[e4] < a[e2]) { int t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
if (a3 < a[e2]) {
if (a3 < a[e1]) {
a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
} else {
a[e3] = a[e2]; a[e2] = a3;
}
} else if (a3 > a[e4]) {
if (a3 > a[e5]) {
a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
} else {
a[e3] = a[e4]; a[e4] = a3;
}
}
// Pointers
int lower = low; // The index of the last element of the left part
int upper = end; // The index of the first element of the right part
/*
* Partitioning with 2 pivots in case of different elements.
*/
if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
/*
* Use the first and fifth of the five sorted elements as
* the pivots. These values are inexpensive approximation
* of tertiles. Note, that pivot1 < pivot2.
*/
int pivot1 = a[e1];
int pivot2 = a[e5];
/*
* The first and the last elements to be sorted are moved
* to the locations formerly occupied by the pivots. When
* partitioning is completed, the pivots are swapped back
* into their final positions, and excluded from the next
* subsequent sorting.
*/
a[e1] = a[lower];
a[e5] = a[upper];
/*
* Skip elements, which are less or greater than the pivots.
*/
while (a[++lower] < pivot1);
while (a[--upper] > pivot2);
/*
* Backward 3-interval partitioning
*
* left part central part right part
* +------------------------------------------------------------+
* | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
* +------------------------------------------------------------+
* ^ ^ ^
* | | |
* lower k upper
*
* Invariants:
*
* all in (low, lower] < pivot1
* pivot1 <= all in (k, upper) <= pivot2
* all in [upper, end) > pivot2
*
* Pointer k is the last index of ?-part
*/
for (int unused = --lower, k = ++upper; --k > lower; ) {
int ak = a[k];
if (ak < pivot1) { // Move a[k] to the left side
while (lower < k) {
if (a[++lower] >= pivot1) {
if (a[lower] > pivot2) {
a[k] = a[--upper];
a[upper] = a[lower];
} else {
a[k] = a[lower];
}
a[lower] = ak;
break;
}
}
} else if (ak > pivot2) { // Move a[k] to the right side
a[k] = a[--upper];
a[upper] = ak;
}
}
/*
* Swap the pivots into their final positions.
*/
a[low] = a[lower]; a[lower] = pivot1;
a[end] = a[upper]; a[upper] = pivot2;
/*
* Sort non-left parts recursively (possibly in parallel),
* excluding known pivots.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
sorter.forkSorter(bits | 1, lower + 1, upper);
sorter.forkSorter(bits | 1, upper + 1, high);
} else {
sort(sorter, a, bits | 1, lower + 1, upper);
sort(sorter, a, bits | 1, upper + 1, high);
}
} else { // Use single pivot in case of many equal elements
/*
* Use the third of the five sorted elements as the pivot.
* This value is inexpensive approximation of the median.
*/
int pivot = a[e3];
/*
* The first element to be sorted is moved to the
* location formerly occupied by the pivot. After
* completion of partitioning the pivot is swapped
* back into its final position, and excluded from
* the next subsequent sorting.
*/
a[e3] = a[lower];
/*
* Traditional 3-way (Dutch National Flag) partitioning
*
* left part central part right part
* +------------------------------------------------------+
* | < pivot | ? | == pivot | > pivot |
* +------------------------------------------------------+
* ^ ^ ^
* | | |
* lower k upper
*
* Invariants:
*
* all in (low, lower] < pivot
* all in (k, upper) == pivot
* all in [upper, end] > pivot
*
* Pointer k is the last index of ?-part
*/
for (int k = ++upper; --k > lower; ) {
int ak = a[k];
if (ak != pivot) {
a[k] = pivot;
if (ak < pivot) { // Move a[k] to the left side
while (a[++lower] < pivot);
if (a[lower] > pivot) {
a[--upper] = a[lower];
}
a[lower] = ak;
} else { // ak > pivot - Move a[k] to the right side
a[--upper] = ak;
}
}
}
/*
* Swap the pivot into its final position.
*/
a[low] = a[lower]; a[lower] = pivot;
/*
* Sort the right part (possibly in parallel), excluding
* known pivot. All elements from the central part are
* equal and therefore already sorted.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
sorter.forkSorter(bits | 1, upper, high);
} else {
sort(sorter, a, bits | 1, upper, high);
}
}
high = lower; // Iterate along the left part
}
}
6、binarySearch()
对排序好的数组,采用二分查找的方式查找某个元素,可以在某个数组中查找,也可以在某个范围内查找。
public static int binarySearch(int[] a, int key) {
return binarySearch0(a, 0, a.length, key);
}
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.
}
原理比较简单,也不解释了就是二分查找的算法。
inarySearch0(a, 0, a.length, key);
}
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.
}
原理比较简单,也不解释了就是二分查找的算法。