快速掌握使用工具类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的方法也很简单,后者是递归操做
相关文章
相关标签/搜索