MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java Collections工具类的常用方法

2022-06-084.8k 阅读

Java Collections工具类概述

在Java编程中,Collections工具类是java.util包下的一个非常重要的类,它提供了一系列用于操作集合(如ListSetMap等)的静态方法。这些方法极大地简化了集合的操作,提高了开发效率。Collections类的所有方法都是静态的,这意味着无需创建Collections类的实例就可以直接调用这些方法。它就像是一个功能强大的工具箱,为集合的处理提供了丰富的“工具”。

排序相关方法

sort(List<T> list)

这个方法用于对指定的List进行升序排序。它要求List中的元素必须实现Comparable接口,因为排序是基于元素自身的自然顺序。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsSortExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);

        System.out.println("排序前: " + numbers);
        Collections.sort(numbers);
        System.out.println("排序后: " + numbers);
    }
}

在上述代码中,我们创建了一个ArrayList并添加了一些整数。然后调用Collections.sort方法对其进行排序,最终输出排序前后的列表。

从本质上来说,Collections.sort方法内部采用了一种高效的排序算法(通常是TimSort)。TimSort结合了合并排序和插入排序的优点,在处理不同数据特性时都能表现出较好的性能。对于较小的子列表,它使用插入排序,因为插入排序在小规模数据上效率较高;对于较大的子列表,它采用合并排序,合并排序具有稳定的排序特性且时间复杂度为O(n log n)。

sort(List<T> list, Comparator<? super T> c)

这个重载方法允许我们使用自定义的Comparator进行排序。当List中的元素类型没有实现Comparable接口,或者我们希望以不同于自然顺序的方式对元素进行排序时,这个方法就非常有用。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class CollectionsCustomSortExample {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 20));
        students.add(new Student("Bob", 18));
        students.add(new Student("Charlie", 22));

        System.out.println("排序前: " + students);

        Collections.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                return s1.getAge() - s2.getAge();
            }
        });

        System.out.println("按年龄升序排序后: " + students);
    }
}

在上述代码中,我们定义了一个Student类,它没有实现Comparable接口。然后我们通过创建一个匿名内部类实现Comparator接口,定义了按照年龄升序排序的逻辑。Collections.sort方法根据我们提供的ComparatorList中的Student对象进行排序。

这里的Comparator接口定义了一个compare方法,该方法接收两个待比较的对象,并返回一个整数值来表示它们的顺序关系。如果返回值小于0,表示第一个对象小于第二个对象;等于0表示相等;大于0表示第一个对象大于第二个对象。通过实现这个方法,我们可以灵活地定义各种排序逻辑。

查找和替换相关方法

binarySearch(List<? extends Comparable<? super T>> list, T key)

二分查找法是一种高效的查找算法,Collections.binarySearch方法就是基于二分查找来在有序的List中查找指定元素的索引。它要求List必须是有序的(通常是通过Collections.sort方法排序后的),否则结果是未定义的。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsBinarySearchExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(3);
        numbers.add(5);
        numbers.add(7);
        numbers.add(9);

        int key = 5;
        System.out.println("查找元素: " + key);
        int index = Collections.binarySearch(numbers, key);
        if (index >= 0) {
            System.out.println("元素 " + key + " 找到,索引为: " + index);
        } else {
            System.out.println("元素 " + key + " 未找到");
        }
    }
}

在这段代码中,我们创建了一个有序的List,然后使用Collections.binarySearch方法查找元素5。如果找到元素,该方法返回元素在列表中的索引;如果未找到,返回一个负整数,这个负整数是插入点(即如果将该元素插入列表,应该插入的位置)的负值再减1。

二分查找的本质是通过每次将查找范围减半,快速缩小查找区间。它首先比较中间元素与目标元素,如果相等则返回中间元素的索引;如果目标元素小于中间元素,则在左半部分继续查找;否则在右半部分查找。这种不断缩小查找范围的方式使得查找效率大大提高,时间复杂度为O(log n)。

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

该方法用于将指定List中所有的旧值替换为新值。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsReplaceAllExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("apple");
        fruits.add("banana");
        fruits.add("apple");
        fruits.add("cherry");

        System.out.println("替换前: " + fruits);
        Collections.replaceAll(fruits, "apple", "orange");
        System.out.println("替换后: " + fruits);
    }
}

在上述代码中,我们有一个包含水果名称的List,通过Collections.replaceAll方法将所有的“apple”替换为“orange”。这个方法遍历整个List,对于每个元素进行比较,如果与旧值相等,则将其替换为新值。

反转和旋转相关方法

reverse(List<?> list)

reverse方法用于反转指定List中元素的顺序。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsReverseExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(4);

        System.out.println("反转前: " + numbers);
        Collections.reverse(numbers);
        System.out.println("反转后: " + numbers);
    }
}

在这段代码中,我们创建了一个List并添加了一些整数,然后调用Collections.reverse方法反转列表元素的顺序。从实现原理上看,它通常是通过交换列表两端的元素,逐步向中间靠拢,从而实现列表的反转。

rotate(List<?> list, int distance)

rotate方法用于将指定List中的元素旋转指定的距离。正数距离表示向右旋转,负数距离表示向左旋转。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsRotateExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(4);

        System.out.println("旋转前: " + numbers);
        Collections.rotate(numbers, 2);
        System.out.println("向右旋转2个位置后: " + numbers);
    }
}

在上述代码中,我们将List向右旋转了2个位置。rotate方法实现的本质是通过一系列元素的移动来达到旋转的效果。它会把列表中从末尾开始的distance个元素移动到列表的开头,而其他元素相应地向后移动。

集合操作相关方法

max(Collection<? extends T> coll)

该方法用于返回指定集合中的最大元素。集合中的元素必须实现Comparable接口,以便进行比较。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsMaxExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(10);
        numbers.add(3);

        Integer max = Collections.max(numbers);
        System.out.println("集合中的最大元素: " + max);
    }
}

在这段代码中,我们创建了一个包含整数的List,然后使用Collections.max方法获取集合中的最大元素。该方法通过遍历集合,依次比较每个元素,最终确定最大的元素。

min(Collection<? extends T> coll)

max方法相反,min方法用于返回指定集合中的最小元素,同样要求集合中的元素实现Comparable接口。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsMinExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(10);
        numbers.add(3);

        Integer min = Collections.min(numbers);
        System.out.println("集合中的最小元素: " + min);
    }
}

这里通过Collections.min方法获取到集合中的最小元素。其实现原理也是通过遍历集合,逐个比较元素,找到最小的那个。

fill(List<? super T> list, T obj)

fill方法用于使用指定的对象填充指定的List,将列表中的每个元素都替换为指定的对象。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsFillExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        System.out.println("填充前: " + list);
        Collections.fill(list, "xyz");
        System.out.println("填充后: " + list);
    }
}

在上述代码中,我们将List中的所有元素都填充为“xyz”。该方法遍历整个List,为每个位置的元素赋值为指定的对象。

同步相关方法

synchronizedCollection(Collection<T> c)

在多线程环境下,集合类通常不是线程安全的。synchronizedCollection方法可以返回一个线程安全的集合包装器,对这个包装器的所有操作都是线程同步的。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CollectionsSynchronizedCollectionExample {
    public static void main(String[] args) {
        Collection<Integer> numbers = new ArrayList<>();
        Collection<Integer> synchronizedNumbers = Collections.synchronizedCollection(numbers);

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService.submit(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronizedNumbers.add(i);
            }
        });

        executorService.submit(() -> {
            for (int i = 1000; i < 2000; i++) {
                synchronizedNumbers.add(i);
            }
        });

        executorService.shutdown();
    }
}

在这段代码中,我们创建了一个普通的ArrayList,然后通过Collections.synchronizedCollection方法将其包装成线程安全的集合。在多线程环境下,多个线程可以安全地对这个同步集合进行操作,避免了数据竞争和不一致的问题。其实现原理是通过在集合的关键方法(如addremove等)上添加同步锁,确保同一时间只有一个线程可以访问这些方法。

synchronizedList(List<T> list)

类似于synchronizedCollectionsynchronizedList方法专门用于返回一个线程安全的List包装器。

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CollectionsSynchronizedListExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        List<Integer> synchronizedNumbers = Collections.synchronizedList(numbers);

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService.submit(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronizedNumbers.add(i);
            }
        });

        executorService.submit(() -> {
            for (int i = 1000; i < 2000; i++) {
                synchronizedNumbers.add(i);
            }
        });

        executorService.shutdown();
    }
}

这里我们将ArrayList包装成线程安全的List,在多线程操作时保证了数据的一致性。同样,它也是通过在List的各个操作方法上添加同步机制来实现线程安全的。

synchronizedSet(Set<T> s)

该方法返回一个线程安全的Set包装器,适用于多线程环境下对Set的操作。

代码示例

import java.util.HashSet;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CollectionsSynchronizedSetExample {
    public static void main(String[] args) {
        Set<Integer> numbers = new HashSet<>();
        Set<Integer> synchronizedNumbers = Collections.synchronizedSet(numbers);

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService.submit(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronizedNumbers.add(i);
            }
        });

        executorService.submit(() -> {
            for (int i = 1000; i < 2000; i++) {
                synchronizedNumbers.add(i);
            }
        });

        executorService.shutdown();
    }
}

通过Collections.synchronizedSet方法,我们将普通的HashSet转换为线程安全的Set,在多线程环境下能够安全地进行元素的添加等操作。

synchronizedMap(Map<K, V> m)

用于返回一个线程安全的Map包装器,确保在多线程环境下对Map的操作是线程安全的。

代码示例

import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CollectionsSynchronizedMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        Map<String, Integer> synchronizedMap = Collections.synchronizedMap(map);

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService.submit(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronizedMap.put("key" + i, i);
            }
        });

        executorService.submit(() -> {
            for (int i = 1000; i < 2000; i++) {
                synchronizedMap.put("key" + i, i);
            }
        });

        executorService.shutdown();
    }
}

这里我们将普通的HashMap包装成线程安全的Map,使得多线程可以安全地对Map进行插入、查询等操作。其实现方式同样是在Map的关键方法上添加同步锁。

不可变集合相关方法

unmodifiableCollection(Collection<? extends T> c)

该方法返回一个不可修改的集合视图。对这个视图进行任何修改操作(如添加、删除元素)都会抛出UnsupportedOperationException

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.Collection;

public class CollectionsUnmodifiableCollectionExample {
    public static void main(String[] args) {
        Collection<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        Collection<Integer> unmodifiableNumbers = Collections.unmodifiableCollection(numbers);
        System.out.println("不可修改集合: " + unmodifiableNumbers);

        // 以下操作会抛出UnsupportedOperationException
        // unmodifiableNumbers.add(4);
    }
}

在上述代码中,我们创建了一个普通的ArrayList,然后通过Collections.unmodifiableCollection方法得到一个不可修改的集合视图。如果尝试对这个视图进行修改操作,就会抛出异常。这种不可修改的视图在需要保护集合数据不被意外修改的场景中非常有用,比如在将集合传递给其他模块,而又不希望其他模块修改集合内容时。

unmodifiableList(List<? extends T> list)

专门用于返回一个不可修改的List视图,对这个视图的修改操作同样会抛出UnsupportedOperationException

代码示例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsUnmodifiableListExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        List<Integer> unmodifiableNumbers = Collections.unmodifiableList(numbers);
        System.out.println("不可修改列表: " + unmodifiableNumbers);

        // 以下操作会抛出UnsupportedOperationException
        // unmodifiableNumbers.add(4);
    }
}

这里我们将ArrayList转换为不可修改的List视图,保证了列表数据的不可变性。

unmodifiableSet(Set<? extends T> s)

返回一个不可修改的Set视图,对其进行修改操作会抛出异常。

代码示例

import java.util.HashSet;
import java.util.Collections;
import java.util.Set;

public class CollectionsUnmodifiableSetExample {
    public static void main(String[] args) {
        Set<Integer> numbers = new HashSet<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        Set<Integer> unmodifiableNumbers = Collections.unmodifiableSet(numbers);
        System.out.println("不可修改集合: " + unmodifiableNumbers);

        // 以下操作会抛出UnsupportedOperationException
        // unmodifiableNumbers.add(4);
    }
}

通过Collections.unmodifiableSet方法,我们将HashSet转换为不可修改的Set视图,防止集合被意外修改。

unmodifiableMap(Map<? extends K,? extends V> m)

用于返回一个不可修改的Map视图,对这个视图进行修改操作会引发异常。

代码示例

import java.util.HashMap;
import java.util.Collections;
import java.util.Map;

public class CollectionsUnmodifiableMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        map.put("three", 3);

        Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(map);
        System.out.println("不可修改地图: " + unmodifiableMap);

        // 以下操作会抛出UnsupportedOperationException
        // unmodifiableMap.put("four", 4);
    }
}

在这段代码中,我们将HashMap转换为不可修改的Map视图,确保了Map数据的不可变性。

总结

Java Collections工具类提供了丰富多样的方法来操作集合,涵盖了排序、查找、替换、反转、旋转、集合操作、同步以及不可变集合等多个方面。这些方法不仅提高了集合操作的效率,还增强了代码的可读性和可维护性。在实际编程中,深入理解并合理运用这些方法,能够帮助我们更好地处理集合数据,无论是在单线程还是多线程环境下,都能确保程序的正确性和高效性。同时,了解这些方法的实现本质,对于优化代码和解决潜在的性能问题也具有重要意义。通过不断实践和学习,我们可以更加熟练地使用Collections工具类,提升自己的Java编程能力。