一、java.util.Arrays类
Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用。
1、Array类与Arrays类的区别?
Array类提供了提供静态方法,把数组作为Object来动态创建和访问 Java数组的方法。
Arrays类包含用于操作数组的各种方法(通过index索引),如排序、搜索等。 该类还包含一个静态工厂,可以将数组视为List<T>。
2、Arrays类的常用方法
只整理了某一种重载的常用方法,具体查看API
1)将数组转换为集合
注意:
(1)该方法不适用于基本数据类型(byte,short,int,long,float,double,boolean)
public static void main(String[] args) throws UnsupportedEncodingException {
int[] ints = { 1, 2, 3 };
List<int[]> list = Arrays.asList(ints);
System.out.println("长度:" + list.size() + ";元素:" + list); //长度:1;元素:[[I@1c20c684]
Integer[] ints2 = { 1, 2, 3, 4, 5 };
List<Integer> list2 = Arrays.asList(ints2);
System.out.println("长度:" + list2.size() + ";元素:" + list2); //长度:5;元素:[1, 2, 3, 4, 5]
}
asList 方法接受的参数是一个泛型的变长参数,我们知道基本数据类型是无法泛型化的,也就是说8个基本类型是无法作为asList方法的参数的, 要想作为泛型参数就必须使用其所对应的包装类型。但是这个实例中为什么没有出错呢?因为该实例是将int 类型的数组当做其参数,而在Java中数组是一个对象,它是可以泛型化的。所以该例子是不会产生错误的。既然例子是将整个int 类型的数组当做泛型参数,那么经过asList转换就只有一个int 的列表了。
结论:在使用asList()时尽量不要将基本数据类型数组转List。
(2)该方法将数组与列表链接起来,当更新其中之一时,另一个自动更新。
public static void main(String[] args) throws UnsupportedEncodingException {
String[] arr = {"a","java","array"};
List<String> list = Arrays.asList(arr);
arr[0] = "edit-a";
list.set(2,"edit-java");
System.out.println(Arrays.toString(arr)); //[edit-a, java, edit-java]
System.out.println(list.toString()); //[edit-a, java, edit-java]
}
(3)不支持add和remove方法
public static void main(String[] args){
String[] arr = {"a","java","array"};
List<String> list = Arrays.asList(arr);
list.add("add"); // 运行报错:java.lang.UnsupportedOperationException
list.remove(arr[0]);
}
通过asList()源码可发现:
Arrays.ArrayList 是工具类 Arrays 的一个内部静态类,它没有完全实现List的方法,而 ArrayList直接实现了List 接口,实现了List所有方法。
Arrays.ArrayList将外部数组的引用直接通过“=”赋予内部的泛型数组,所以本质指向同一个数组。
Arrays.asList() 方法使用场景:
Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List 。但是,生成的List的长度是固定的;能够进行修改操作(比如,修改某个位置的元素);不能执行影响长度的操作(如add、remove等操作),否则会抛出UnsupportedOperationException异常。
所以 Arrays.asList 比较适合那些已经有数组数据或者一些元素,而需要快速构建一个List,只用于读取操作,而不进行添加或删除操作的场景。
2)拷贝数组
其内部调用了 System.arraycopy() 方法,从下标 0 开始,如果超过原数组长度,则会用 null 进行填充。
-
-
static <T> T[]
copyOf(T[] original, int newLength)
复制指定的数组,用空值截断或填充(如有必要),以便复制具有指定的长度。
static <T> T[]
copyOfRange(T[] original, int from, int to)
将指定数组的指定范围复制到新数组中。
-
public static void main(String[] args){
String[] arr = {"a","java","array","b"};
String[] arr1 = Arrays.copyOf(arr, 2); //[a, java]
String[] arr2 = Arrays.copyOf(arr, 5); //[a, java, array, b, null]
String[] arr3 = Arrays.copyOfRange(arr,1,3); //[java, array]
String[] arr4 = Arrays.copyOfRange(arr,2,5); //[array, b, null]
}
3)判断两个数组是否相等
判断两个(多维)数组是否相等
数组元素为基本数据类型时,依次比较值
数组元素为引用数据类型时,依次调用元素的 equals() 方法进行比较
即如果两个(多维)数组被认为是相等的,则两个(多维)数组中应包含相同顺序的相同元素
public static void main(String[] args){
Integer[] data1 = {1, 2, 3};
Integer[] data2 = {1, 3, 2};
System.out.println(Arrays.equals(data1, data2)); // false
Integer[][] data3 = {{1,2,3}, {1,2,3}};
Integer[][] data4 = {{1,2,3}, {1,2,3}};
System.out.println(Arrays.deepEquals(data3, data4)); // true
}
4)获取数组的哈希值
-
-
static int
hashCode(Object[] a)
根据指定数组的内容返回哈希码。
static int
deepHashCode(Object[] a)
根据指定数组的“深度内容”返回哈希码。
-
5)获取数组元素的字符串形式
public static void main(String[] args){
Integer[] data1 = {1, 2, 3};
Integer[][] data2 = {{1,2,3}, {1,2,3}};
//哈希值
System.out.println(Arrays.hashCode(data1)); //30817
System.out.println(Arrays.deepHashCode(data2)); //987105
//字符串形式
System.out.println(Arrays.toString(data1)); //[1, 2, 3]
System.out.println(Arrays.deepToString(data2)); //[[1, 2, 3], [1, 2, 3]]
}
6)数组的排序
-
-
static void
sort(int[] a)
按照数字顺序排列指定的数组。
static void
sort(Object[] a)
对指定对象升序排列的阵列,根据natural ordering的元素。
static <T> void
sort(T[] a, Comparator<? super T> c)
根据指定的比较器引发的顺序对指定的对象数组进行排序。
static void
sort(Object[] a, int fromIndex, int toIndex)
对指定对象升序排列的数组的指定范围内,根据natural ordering的元素。
-
(1)八大基本类型的数组
public static void main(String[] args){
int[] arr = {19, 2, 203,4,2,1};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); //[1, 2, 2, 4, 19, 203]
}
八大基本数据类型数组底层调用的都是 Java中双基准快速排序方法(DualPivotQuicksort.sort())的具体实现
(2)对象类型数组
public static void main(String[] args){
// String 类实现了 Comparable 接口,内部的 compareTo 方法是按照字典码进行比较的。
String[] arr2 = {"sd","a","d","ab","ac"};
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2)); //[a, ab, ac, d, sd]
}
对象类型数组底层调用的是:
(3)对实现或没有实现Comparable接口的对象数组,可以通过Comparator实现排序(升序/降序)
public static void main(String[] args){
User user1 = new User("admin","admin123",19);
User user2 = new User("ls","ls123",15);
User user3 = new User("ww","ww123",18);
User[] array = {user1,user2,user3};
Arrays.sort(array, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
//根据年龄降序排列
return -Integer.compare(o1.getAge(),o2.getAge());
}
});
System.out.println(Arrays.toString(array));
}
public class User {
private String username;
private String password;
private int age;
...
}
通过Comparator实现排序数组底层调用的是:
7)二分法查找数组中的某个元素
该方法和 sort 方法一样,适用于各种基本数据类型以及对象。
注意:二分法是对以及有序的数组进行查找(比如先用Arrays.sort()进行排序,然后调用此方法进行查找)。
若找到元素返回其下标,否则返回 -1。
-
-
static int
binarySearch(long[] a, long key)
使用二进制搜索算法搜索指定数组的指定数组。
static int
binarySearch(Object[] a, Object key)
使用二叉搜索算法搜索指定对象的指定数组。
static int
binarySearch(Object[] a, int fromIndex, int toIndex, Object key)
使用二进制搜索算法搜索指定对象的指定数组的范围。
-
public static void main(String[] args){
int[] arr = {19, 2, 203,4,2,1};
Arrays.sort(arr); ////[1, 2, 2, 4, 19, 203]
System.out.println(Arrays.binarySearch(arr,1)); //0
System.out.println(Arrays.binarySearch(arr,2)); //2
String[] arr2 = {"sd","a","d","ab","ac"};
Arrays.sort(arr2); // [a, ab, ac, d, sd]
System.out.println(Arrays.binarySearch(arr2,"d")); //3
}
8)填充数组元素为某个值
public static void main(String[] args){
int[] arr = {19, 2, 203,4,2,1};
String[] arr2 = {"sd","a","d","ab","ac"};
Arrays.fill(arr,17);
Arrays.fill(arr2,2,4,"java");
System.out.println(Arrays.toString(arr)); //[17, 17, 17, 17, 17, 17]
System.out.println(Arrays.toString(arr2));//[sd, a, java, java, ac]
}
3、JDK8 出现的一些方法
-
-
static <T> void
setAll(T[] array, IntFunction<? extends T> generator)
使用提供的生成函数来计算每个元素,设置指定数组的所有元素。
static <T> void
parallelSetAll(T[] array, IntFunction<? extends T> generator)
使用提供的生成函数来并行设置指定数组的所有元素来计算每个元素。
static <T> void
parallelPrefix(T[] array, BinaryOperator<T> op)
使用提供的功能,并行地计算给定阵列的每个元素。
static <T> void
parallelPrefix(T[] array, int fromIndex, int toIndex, BinaryOperator<T> op)
对于数组的给定子范围执行
parallelPrefix(Object[], BinaryOperator)
。static <T> Spliterator<T>
spliterator(T[] array)
返回
Spliterator
覆盖所有指定数组static <T> Stream<T>
stream(T[] array)
返回顺序
Stream
与指定的数组作为源。static <T> Stream<T>
stream(T[] array, int startInclusive, int endExclusive)
返回顺序
Stream
与指定的数组作为源的指定范围。
-
public static void main(String[] args){
/**
* Arrays.setAll(T[] array, IntFunction<? extends T> generator)
* 让数组中的所有元素,串行地使用方法提供的生成器函数来计算每个元素 (一元操作)
*/
Integer[] data = {1, 2, 3, 4};
Arrays.setAll(data, i -> data[i] * 2); // i为索引值
System.out.println(Arrays.toString(data)); // [2, 4, 6, 8]
/**
* Arrays.parallelSetAll(T[] array, IntFunction<? extends T> generator)
* 让数组中的所有元素,并行地使用方法提供的生成器函数来计算每个元素 (一元操作),当数据规模较大时,会有更好的性能
*/
Integer[] data2 = {1, 2, 3, 4};
Arrays.parallelSetAll(data2, i -> data2[i] * 2); // i为索引值
System.out.println(Arrays.toString(data2)); // [2, 4, 6, 8]
/**
* Arrays.parallelPrefix(T[] array, BinaryOperator op)
* 让数组中的所有元素,并行地使用方法提供的生成器函数来计算每个元素 (二元操作),当数据规模较大时,会有更好的性能
*/
Integer[] data3 = {2, 3, 4, 5};
// 第一个元素2不变,将其与第二个元素3一起作为参数x, y传入,得到乘积6,作为数组新的第二个元素
// 再将6和第三个元素4一起作为参数x, y传入,得到乘积24,作为数组新的第三个元素,以此类推
Arrays.parallelPrefix(data3, (x, y) -> x * y);
System.out.println(Arrays.toString(data3)); // [2, 6, 24, 120]
/**
* Arrays.parallelPrefix(T[] array, int fromIndex, int toIndex, BinaryOperator op
* 让指定范围内的数组元素,并行地使用方法提供的生成器函数来计算每个元素 (二元操作),当数据规模较大时,会有更好的性能
*/
Integer[] data4 = {2, 3, 4, 5,6};
Arrays.parallelPrefix(data4, 1, data4.length-1, (x, y) -> x * y); //[1,data2.length-1)
System.out.println(Arrays.toString(data4)); // [2, 3, 12, 60, 6]
/**
* Arrays.stream(T[] array)
* 返回数组的流 (Stream),然后我们就可以使用 Stream 相关的许多方法了
*/
Integer[] data5 = {1, 2, 3, 4};
List<Integer> list = Arrays.stream(data5).collect(Collectors.toList());
System.out.println(list); // [1, 2, 3, 4]
}
二、java.util.Collections 类
1、Collection和Collections的区别?
Collection是集合类的上级接口,继承与他有关的接口主要有List和Set
Collections是针对集合类的一个工具类,封装了Set,List,Map的操作的工具方法,提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作
2、Collections类的常用方法
java.util.Collections 类提供了大量方法对集合进行排序、查询和修改等操作,还提供了将集合对象置为不可变、对集合对象实现同步控制等方法。而且每个方法基本上都是静态方法,能直接通过类名Collections调用。
1)排序操作
Collections.sort 方法底层调用的是 Arrays.sort 方法。
-
-
static <T extends Comparable<? super T>>
voidsort(List<T> list)
根据其元素的natural ordering对指定的列表进行排序。
static <T> void
sort(List<T> list, Comparator<? super T> c)
根据指定的比较器引起的顺序对指定的列表进行排序。
static void
swap(List<?> list, int i, int j)
交换指定列表中指定位置的元素。
static void
reverse(List<?> list)
反转指定列表中元素的顺序。
static void
shuffle(List<?> list)
使用默认的随机源随机排列指定的列表。
-
public static void main(String[] args){
List list = Arrays.asList("one two three four five six siven".split(" "));
Collections.sort(list);
System.out.println(list.toString()); //[five, four, one, siven, six, three, two]
Collections.reverse(list);
System.out.println(list.toString()); //[two, three, six, siven, one, four, five]
}
2)查找、替换操作
-
-
static <T> int
binarySearch(List<? extends Comparable<? super T>> list, T key)
使用二叉搜索算法搜索指定对象的指定列表。
static <T> void
fill(List<? super T> list, T obj)
用指定的元素代替指定列表的所有元素。
static int
frequency(Collection<?> c, Object o)
返回指定集合中与指定对象相等的元素数。
static int
indexOfSubList(List<?> source, List<?> target)
返回指定源列表中指定目标列表的第一次出现的起始位置,如果没有此类事件,则返回-1。
static int
lastIndexOfSubList(List<?> source, List<?> target)
返回指定源列表中指定目标列表的最后一次出现的起始位置,如果没有此类事件则返回-1。
static <T extends Object & Comparable<? super T>>
Tmax(Collection<? extends T> coll)
根据其元素的 自然顺序返回给定集合的最大元素。
static <T extends Object & Comparable<? super T>>
Tmin(Collection<? extends T> coll)
根据其元素的 自然顺序返回给定集合的最小元素。
static <T> boolean
replaceAll(List<T> list, T oldVal, T newVal)
将列表中一个指定值的所有出现替换为另一个。
-
3)同步控制
Collectons类提供了多个 synchronizedXxx()方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。
-
-
static <T> Collection<T>
synchronizedCollection(Collection<T> c)
返回由指定集合支持的同步(线程安全)集合。
static <T> List<T>
synchronizedList(List<T> list)
返回由指定列表支持的同步(线程安全)列表。
static <K,V> Map<K,V>
synchronizedMap(Map<K,V> m)
返回由指定地图支持的同步(线程安全)映射。
static <T> Set<T>
synchronizedSet(Set<T> s)
返回由指定集合支持的同步(线程安全)集。
-
public static void main(String[] args){
Integer[] arr = {8, -2, 6, 5, 9, 0};
//创建了四个同步的集合对象
Collection c = Collections.synchronizedCollection(new ArrayList());
List list = Collections.synchronizedList(new ArrayList());
Set set = Collections.synchronizedSet(new HashSet());
Map map = Collections.synchronizedMap(new HashMap());
//当要做迭代的时候得使用synchronized.
synchronized(list) {
//TODO
}
}
4)其他方法
public static void main(String[] args){
//获取空集对象(没有元素的集合,注意:集合不为null):
List<Object> list1 = Collections.EMPTY_LIST;
List<Object> list2 = Collections.emptyList();
System.out.println(list1 == null); //false
// 将所有指定的元素添加到指定的集合。
List list = Arrays.asList("one two three four five six siven".split(" "));
ArrayList list3 = new ArrayList();
list3.addAll(list);
System.out.println(list3.toString()); //[one, two, three, four, five, six, siven]
}
参考文章:
Java中双基准快速排序方法(DualPivotQuicksort.sort())的具体实现
java.util.ComparableTimSort中的sort()方法简单分析
站在前辈的肩膀上,每天进步一点点
ends~