ZetCode

Java Collections.swap 方法

上次修改时间:2025 年 4 月 20 日

Collections.swap 方法是 Java Collections 框架中的一个实用方法。它交换列表中指定位置的元素。此方法在您需要重新排列元素时特别有用。

该方法接受三个参数:列表和两个索引。如果任何索引超出范围,它将抛出 IndexOutOfBoundsException 异常。对于随机访问列表,交换操作在恒定时间内执行。

Collections.swap 方法概述

swap 方法定义在 java.util.Collections 类中。它是一个静态方法,作用于任何 List 实现。该方法修改原始列表,而不是返回一个新列表。

方法签名是:public static void swap(List<?> list, int i, int j)。两个索引都必须是非负数且小于列表大小。该方法适用于任何类型的列表元素。

基本交换操作

此示例演示了 Collections.swap 方法的最基本用法。我们创建一个简单的字符串列表并交换两个元素。该示例显示了交换操作之前和之后的列表。

BasicSwapExample.java
package com.zetcode;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class BasicSwapExample {

    public static void main(String[] args) {
        
        List<String> colors = Arrays.asList("Red", "Green", "Blue", "Yellow");
        
        System.out.println("Before swap: " + colors);
        
        // Swap elements at positions 1 and 3
        Collections.swap(colors, 1, 3);
        
        System.out.println("After swap: " + colors);
    }
}

此代码使用 Arrays.asList 创建一个不可变的颜色名称列表。然后,我们交换位置 1 ("Green") 和 3 ("Yellow") 的元素。输出显示了交换操作之前和之后的列表。

请注意,虽然列表在大小上是不可变的(无法添加/删除元素),但我们仍然可以修改现有元素。交换操作会更改固定大小列表中元素的位置。

在 ArrayList 中交换元素

此示例展示了如何将 Collections.swap 与 ArrayList 一起使用。ArrayList 在 Java 应用程序中经常被使用并且可以调整大小。我们将交换元素并演示多次交换。

ArrayListSwapExample.java
package com.zetcode;

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

public class ArrayListSwapExample {

    public static void main(String[] args) {
        
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        numbers.add(40);
        numbers.add(50);
        
        System.out.println("Original list: " + numbers);
        
        // Swap first and last elements
        Collections.swap(numbers, 0, numbers.size() - 1);
        System.out.println("After first swap: " + numbers);
        
        // Swap middle elements
        Collections.swap(numbers, 1, 3);
        System.out.println("After second swap: " + numbers);
    }
}

此示例演示了在可变的 ArrayList 中交换元素。我们首先交换第一个和最后一个元素,然后交换位置 1 和 3 的元素。每个交换操作都会在原位修改列表。

输出显示了对列表的逐步更改。ArrayList 尤其擅长进行交换操作,这得益于它们的随机访问特性。每次交换的时间复杂度为 O(1)。

交换自定义对象

Collections.swap 方法适用于任何对象类型,包括自定义类。此示例展示了交换自定义 Person 类的对象。该方法不需要在类中进行任何特殊的实现。

CustomObjectSwapExample.java
package com.zetcode;

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

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name + " (" + age + ")";
    }
}

public class CustomObjectSwapExample {

    public static void main(String[] args) {
        
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 25));
        people.add(new Person("Bob", 30));
        people.add(new Person("Charlie", 35));
        
        System.out.println("Before swap: " + people);
        
        // Swap first and last person
        Collections.swap(people, 0, people.size() - 1);
        
        System.out.println("After swap: " + people);
    }
}

此示例创建一个 Person 对象列表,并交换第一个和最后一个元素。Person 类实现了 toString 以实现可读的输出。交换操作的运作方式与基本类型相同。

输出表明,对象引用在列表中被交换。实际的对象保持不变 - 仅修改它们在列表中的位置。对于 Java 中的任何对象类型,这都是成立的。

处理 IndexOutOfBoundsException 异常

此示例演示了在为 Collections.swap 方法提供无效索引时会发生什么情况。我们将展示正确的错误处理并演示边界检查行为。

SwapExceptionHandling.java
package com.zetcode;

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

public class SwapExceptionHandling {

    public static void main(String[] args) {
        
        List<String> items = new ArrayList<>();
        items.add("Apple");
        items.add("Banana");
        items.add("Cherry");
        
        try {
            // Attempt to swap with invalid indices
            Collections.swap(items, -1, 2);
            System.out.println("Swap successful");
        } catch (IndexOutOfBoundsException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        try {
            // Attempt to swap with index equal to size
            Collections.swap(items, 1, items.size());
            System.out.println("Swap successful");
        } catch (IndexOutOfBoundsException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // Valid swap after error handling
        Collections.swap(items, 0, 2);
        System.out.println("Final list: " + items);
    }
}

此示例通过使用无效索引故意触发 IndexOutOfBoundsException 异常。我们演示了两种情况:负索引和等于列表大小的索引。异常消息有助于识别哪个索引无效。

处理完异常后,我们执行一个有效的交换操作。输出显示了错误消息和最终成功的交换结果。当使用用户提供的索引时,正确的错误处理非常重要。

在 LinkedList 中进行交换

虽然 Collections.swap 适用于任何 List 实现,但其性能各不相同。此示例演示了在 LinkedList 中进行交换,它具有与 ArrayList 不同的性能特征。

LinkedListSwapExample.java
package com.zetcode;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class LinkedListSwapExample {

    public static void main(String[] args) {
        
        List<Integer> numbers = new LinkedList<>();
        Collections.addAll(numbers, 1, 2, 3, 4, 5);
        
        System.out.println("Original list: " + numbers);
        
        // Swap elements far apart
        long startTime = System.nanoTime();
        Collections.swap(numbers, 0, numbers.size() - 1);
        long endTime = System.nanoTime();
        
        System.out.println("After swap: " + numbers);
        System.out.println("Swap time (ns): " + (endTime - startTime));
        
        // Compare with ArrayList
        List<Integer> arrayList = new java.util.ArrayList<>(numbers);
        
        startTime = System.nanoTime();
        Collections.swap(arrayList, 0, arrayList.size() - 1);
        endTime = System.nanoTime();
        
        System.out.println("ArrayList swap time (ns): " + (endTime - startTime));
    }
}

此示例比较了 LinkedList 和 ArrayList 之间的交换性能。虽然两者都能正常工作,但 ArrayList 通常在随机访问操作中更快。该示例测量并显示了每次交换操作所花费的时间。

输出显示,LinkedList 交换需要更长的时间,因为它们需要遍历到指定位置。ArrayList 可以直接访问任何位置,使其在频繁的交换操作中更有效率。

使用 swap 实现洗牌算法

Collections.swap 方法可用于实现更复杂的算法。此示例演示了使用随机交换的简单洗牌算法。我们将它与内置的 Collections.shuffle 进行比较。

CustomShuffleExample.java
package com.zetcode;

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

public class CustomShuffleExample {

    public static void main(String[] args) {
        
        List<Integer> numbers = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            numbers.add(i);
        }
        
        System.out.println("Original list: " + numbers);
        
        // Custom shuffle using swap
        customShuffle(numbers);
        System.out.println("After custom shuffle: " + numbers);
        
        // Reset and use Collections.shuffle
        Collections.sort(numbers);
        Collections.shuffle(numbers);
        System.out.println("After Collections.shuffle: " + numbers);
    }
    
    private static void customShuffle(List<?> list) {
        Random random = new Random();
        for (int i = list.size() - 1; i > 0; i--) {
            int j = random.nextInt(i + 1);
            Collections.swap(list, i, j);
        }
    }
}

此示例使用 Collections.swap 实现了 Fisher-Yates 洗牌算法。我们生成随机索引并从列表的末尾到开头交换元素。然后将自定义实现与 Java 的内置洗牌算法进行比较。

两种方法都会生成随机排序的列表,但内置版本可能使用更好的随机数生成器。该示例演示了如何将 swap 用作更复杂操作的构建块。

在嵌套列表中交换

此高级示例演示了在嵌套列表结构中交换元素。我们将展示如何在不同的子列表之间以及在同一个子列表中交换元素。该示例处理二维列表结构。

NestedListSwapExample.java
package com.zetcode;

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

public class NestedListSwapExample {

    public static void main(String[] args) {
        
        List<List<String>> matrix = new ArrayList<>();
        matrix.add(new ArrayList<>(List.of("A", "B", "C")));
        matrix.add(new ArrayList<>(List.of("D", "E", "F")));
        matrix.add(new ArrayList<>(List.of("G", "H", "I")));
        
        System.out.println("Original matrix:");
        printMatrix(matrix);
        
        // Swap within the same sublist
        Collections.swap(matrix.get(1), 0, 2);
        System.out.println("\nAfter swapping in row 1:");
        printMatrix(matrix);
        
        // Swap between different sublists
        String temp = matrix.get(0).get(2);
        matrix.get(0).set(2, matrix.get(2).get(0));
        matrix.get(2).set(0, temp);
        
        System.out.println("\nAfter swapping between rows:");
        printMatrix(matrix);
    }
    
    private static void printMatrix(List<List<String>> matrix) {
        for (List<String> row : matrix) {
            System.out.println(row);
        }
    }
}

此示例使用表示为嵌套列表的 3x3 矩阵。首先,我们使用 Collections.swap 在单个行内交换元素。然后,我们演示了在不同行之间进行交换,这需要临时存储。

输出显示了每次操作后的矩阵。此技术可以扩展到更复杂的数据结构。该示例突出了 swap 在多维场景中的使用方式。

来源

Java Collections.swap 文档

在本文中,我们深入探讨了 Java Collections.swap 方法。我们介绍了基本用法、不同的列表类型、异常处理和高级应用。理解此方法有助于高效的列表操作。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员,在编程领域拥有多年经验。我从 2007 年开始撰写编程文章,至今已撰写了 1400 多篇文章和 8 本电子书。凭借超过 8 年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程