Java 集合工具类--Arrays和Collections

一、java.util.Arrays类

        Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用。

1、Array类与Arrays类的区别?

      Array类提供了提供静态方法,把数组作为Object来动态创建和访问 Java数组的方法。

      Arrays类包含用于操作数组的各种方法(通过index索引),如排序、搜索等。 该类还包含一个静态工厂,可以将数组视为List<T>。

2、Arrays类的常用方法

     只整理了某一种重载的常用方法,具体查看API

1)将数组转换为集合

    • static <T> List<T> asList(T... a)

      返回由指定数组支持的固定大小的列表。

注意:

  (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)填充数组元素为某个值

    • static void fill(Object[] a, int fromIndex, int toIndex, Object val)

      将指定的对象引用分配给指定的对象数组的指定范围的每个元素。

      static void fill(Object[] a, Object val)

      将指定的对象引用分配给指定的对象数组的每个元素。

    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 出现的一些方法

    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>>
      void
      sort(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>>
      T
      max(Collection<? extends T> coll)

      根据其元素的 自然顺序返回给定集合的最大元素。

      static <T extends Object & Comparable<? super T>>
      T
      min(Collection<? extends T> coll)

      根据其元素的 自然顺序返回给定集合的最小元素。

      static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)

      将列表中一个指定值的所有出现替换为另一个。

3)同步控制

  Collectons类提供了多个 synchronizedXxx()方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。

    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)其他方法

    • static <T> boolean addAll(Collection<? super T> c, T... elements)

      将所有指定的元素添加到指定的集合。

      static <T> List<T> emptyList()

      返回空列表(immutable)。

      static <T> Set<T> emptySet()

      返回一个空集(immutable)。

      static <K,V> Map<K,V> emptyMap()

      返回空的map(不可变)。

    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]
    }

 

参考文章:

    千万不要这样使用Arrays.asList !

    Java中双基准快速排序方法(DualPivotQuicksort.sort())的具体实现

    java.util.ComparableTimSort中的sort()方法简单分析

    Timsort——自适应、稳定、高效排序算法

 

    站在前辈的肩膀上,每天进步一点点

ends~

发布了248 篇原创文章 · 获赞 59 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_42402854/article/details/99944385