快速掌握使用工具類java.util.Arrays

Arrays簡介

Arrays是一個數組類,類裏面有不少操做數組的方法,包括排序和查找。同時類裏面有一個靜態工廠,它容許你將數組當成列表來使用。java

Arrays特殊要點

  1. MIN_ARRAY_SORT_GRAN = 1 << 13
    這是一個數組長度指標,值爲8192,若是數組長度小於該值,並行排序算法將不會進一步劃分排序任務,由於這樣容易引發內存爭用,致使排序變慢。
  2. private Arrays(){}
    私有構造方法
  3. NaturalOrder
    這是一個天然排序比較器,若是你沒有提供自定義的比較器,則默認使用這個
  4. LegacyMergeSort
    這是一個boolean值,true表明使用老版本的歸併排序
  5. INSERTIONSORT_THRESHOLD
    這是一個度量,長度小於7的數組,將用插入排序,而不用歸併排序
  6. ArrayList<E> 這是一個私有內部類,實現了隨機訪問和序列化接口,Arrays.asList(T... a)方法將一個數組轉換爲一個列表

Arrays快速使用

sort排序

sort方法支持int[] short[] long[] char[] byte[] float[] double[]等基本類型的升序排序,內部實現用的是改進事後的快速排序,叫DualPivotQuicksort
常見的幾種使用方式以下算法

int[] a = new int[]{10000,565,234,654,8};
Arrays.sort(a);
log.info(Arrays.toString(a));

int[] b = new int[]{10000,565,234,654,8};
Arrays.sort(b,1,5);
log.info(Arrays.toString(b));

initStudents(students);
Arrays.sort(students);//Student類實現了Comparable接口

initTeachers(teachers);
Arrays.sort(teachers,Comparator.comparingInt(Teacher::getNo));//Teacher類沒有實現Comparable接口,因此須要傳入一個比較器,這裏用了java8的特性
//對象的排序一樣支持部分排序
複製代碼
parallelSort並行排序

數組長度超過8192時推薦使用該方法進行排序,它會將數組分割成小數組進行排序,再將結果合併起來
使用該方法時,注意內部有一個判斷if (n <= MIN_ARRAY_SORT_GRAN ||(p = ForkJoinPool.getCommonPoolParallelism()) == 1),該判斷爲true時調用sort方法,不然,才進入並行排序算法
方法使用狀況同sort(),用例以下數組

Random random = new Random(System.currentTimeMillis());
Student[] students = new Student[9000];
Teacher[] teachers = new Teacher[9000];
for (int i = 0; i < 9000; i++) {
    students[i] = new Student();
    students[i].setNo(random.nextInt()%90000);
    teachers[i] = new Teacher();
    teachers[i].setNo(random.nextInt()%90000);
}
Arrays.parallelSort(students);//超過8192後有時候行,有時候不行?若是加上%90000,就沒問題,但%2100000000也會有問題
Arrays.parallelSort(teachers, Comparator.comparingInt(Teacher::getNo));
複製代碼

parallelSort在對基本類型進行並行排序時沒有什麼問題,但對對象進行操做時,而且隨機值大時,容易拋java.lang.IllegalArgumentException: Comparison method violates its general contract!這個異常,查了一些資料,仍是沒有找到根本緣由,疑惑點以下
1. 若是說少考慮了相等的狀況,爲何%9000就能夠?
2. 爲何%9000能夠,%2100000000不行
3. 去掉取模運算,有時候拋異常,有時候不拋異常
4. 若是有道友能指點一下,還請賜教!
bash

parallelPrefix統計

該方法用得比較少,它經過利用前面全部元素的計算結果,對目標元素進行相同邏輯的運算,並將運算結果傳遞給下一個元素使用, 用例以下dom

int[] a = new int[]{10000,565,234,654,8};
Arrays.parallelPrefix(a, Integer::sum);
log.info(Arrays.toString(a));

//非基本類型使用該方法時,須要返回一個新的對象,由於本質是賦值一個新的對象地址,lambda表達式中的teacher是上一輪的計算結果,可打印出來驗證
Arrays.parallelPrefix(teachers, (teacher, teacher2) -> {
    Teacher temp = new Teacher();
    temp.setNo(teacher.getNo() + teacher2.getNo());
    return temp;
});
log.info(Arrays.toString(teachers));
複製代碼
search查找

內部使用的是二分查找,要求數組必須有序,返回目標元素的索引值,索引值小於0時表明找不到該元素
支持基本類型數組及對象數組的二分查找和區域二分查找函數

int[] a = new int[]{1,2,3,4,8};
int index = Arrays.binarySearch(a,3);
log.info(String.valueOf(index));

index = Arrays.binarySearch(a,1,a.length-1,8);
log.info(String.valueOf(index));
// 沒有實現Comparable接口的對象
initTeacher(teachers);
Teacher key = new Teacher();
key.setNo(3);
index = Arrays.binarySearch(teachers, key, Comparator.comparingInt(Teacher::getNo));
if(index > 0){
    log.info(String.valueOf(teachers[index]));
}
// 實現了Comparable接口的對象
initStudent(students);
Student student = new Student();
student.setNo(2);
index = Arrays.binarySearch(students,student);
if(index > 0){
    log.info(students[index].toString());
}
複製代碼
填充

fill函數至關於初始化函數,沒什麼特別的,但在對象的填充是注意不要被坑,由於它僅僅是一層淺複製,若是源信息作出了修改,是會影響到複製獲得的數組的ui

int[] a = new int[5];
Arrays.fill(a,8);
log.info(Arrays.toString(a));
int[] b = new int[5];
Arrays.fill(b,2,4,8);
log.info(Arrays.toString(b));
//支持long[],int[],short[],char[],byte[],boolean[],double[],float[],Object[]類型的填充
Student[] students = new Student[3];
Student student = new Student();
student.setNo(6);
Arrays.fill(students,student);
student.setNo(1);
log.info(Arrays.toString(students));//爲何是1而不是6?
複製代碼
複製

copy函數也是一個淺複製spa

Teacher[] teachers = new Teacher[5];
for (int i = 0; i < 5; i++) {
    teachers[i] = new Teacher();
    teachers[i].setNo(i);
}
Teacher[] copy = Arrays.copyOf(teachers, 3);
Teacher[] range = Arrays.copyOfRange(teachers, 2, 4);
teachers[2].setNo(8888);
log.info(Arrays.toString(copy));
log.info(Arrays.toString(range));
//支持byte[],short[],int[],long[],char[],float[],double[],boolean[]等類型數組的複製
複製代碼
分割器(迭代器)

tryAdvance方法表示每次消費一個被分割出來的元素 forEachRemaining方法表示剩下的被分割出來的元素所有用指定的方法去消費code

int[] a = new int[]{1,2,3,4,8};
Spliterator.OfInt spliterator = Arrays.spliterator(a);
spliterator.tryAdvance((IntConsumer) value -> System.out.println(value * value));
spliterator.tryAdvance((IntConsumer) value -> System.out.println(value * value));
spliterator.forEachRemaining((IntConsumer) System.out::println);
複製代碼
stream流

java8新添加的特性,這裏只演示一下stream的簡單用法,後面會出一篇專門的文章來分享這個東西。對象

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 8);
List<Integer> collect = integers.stream().map(e -> e * e).collect(Collectors.toList());
log.info(collect.toString());
複製代碼

總結

  1. 排序方法sort和parallelSort是比較實在的,但須要注意parallelSort的前提條件
  2. search方法也須要掌握,同排序同樣,非基本類型的對象須要實現Comparable接口或者額外傳入一個比較器
  3. fill和copy都是淺複製,不建議使用
  4. parallelPrefix該方法適用於規律性比較強的,且利用前一步計算結果的場景,好比累加等
  5. 分割器spliterator感受仍是挺有意思的,傳入一個消費函數進行消費被分割出來的元素
  6. stream流是java8的一大特性,這裏暫時沒有展開,留存下一篇文章
  7. 還有一些toString,deepToString的方法也很簡單,後者是遞歸操做
相關文章
相關標籤/搜索