ZetCode

Java Collections.nCopies 方法

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

Collections.nCopies 方法是 Java Collections Framework 中的一个实用方法。 它创建一个包含单个元素的多个副本的不可变列表。 当您需要一个具有重复值的列表时,这很有用。

该方法返回一个包含指定对象的 n 个副本的列表。 该列表是不可变的,这意味着它的大小和元素无法更改。 列表中的所有元素都引用同一个对象。

Collections.nCopies 概述

nCopies 方法是 java.util.Collections 类的一部分。 这是一个静态工厂方法,用于创建一种特殊的 List 实现。 返回的列表是节省空间的,因为它只存储一个元素引用。

该方法的签名是:static <T> List<T> nCopies(int n, T o)。 第一个参数是副本的数量,第二个参数是要重复的元素。 该列表是可序列化的,并实现了 RandomAccess 以实现高效访问。

基本 nCopies 示例

此示例演示了 nCopies 的最简单用法。 我们创建一个包含字符串 "Hello" 的五个副本的列表。 生成的列表是不可变的,无法修改。

BasicNCopies.java
package com.zetcode;

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

public class BasicNCopies {

    public static void main(String[] args) {
        
        List<String> greetings = Collections.nCopies(5, "Hello");
        
        System.out.println("List size: " + greetings.size());
        System.out.println("List contents: " + greetings);
        
        try {
            greetings.add("Hi"); // Will throw exception
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCannot modify nCopies list: " + e);
        }
    }
}

此代码创建一个包含五个 "Hello" 字符串的不可变列表。 列表的大小在创建时是固定的。 尝试修改列表会抛出 UnsupportedOperationException 异常。

输出显示了列表内容并演示了它的不变性。 这对于创建常量列表或默认值集合很有用。

创建初始化列表

nCopies 可用于初始化可变列表。 通过将不可变列表传递给构造函数,我们可以创建一个可变副本。 这比在循环中添加元素更有效。

InitializeList.java
package com.zetcode;

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

public class InitializeList {

    public static void main(String[] args) {
        
        // Create immutable list with 10 zeros
        List<Integer> immutableZeros = Collections.nCopies(10, 0);
        
        // Create mutable list from immutable one
        List<Integer> mutableZeros = new ArrayList<>(immutableZeros);
        
        System.out.println("Initial list: " + mutableZeros);
        
        // Now we can modify it
        mutableZeros.set(5, 99);
        mutableZeros.add(100);
        
        System.out.println("Modified list: " + mutableZeros);
    }
}

此示例演示了如何使用 nCopies 进行列表初始化。 我们首先创建一个由零组成的不可变列表,然后使用它来构造一个可变的 ArrayList。 然后可以根据需要修改可变列表。

这种方法比使用循环进行初始化更有效,尤其是对于大型列表。 它清楚地表达了创建具有默认值的列表的意图。

使用自定义对象

nCopies 适用于任何对象类型,包括自定义类。 生成列表中的所有元素都将引用同一个对象实例。 在使用可变对象时,理解这一点很重要。

CustomObjects.java
package com.zetcode;

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

class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CustomObjects {

    public static void main(String[] args) {
        
        Person original = new Person("John");
        List<Person> people = Collections.nCopies(3, original);
        
        System.out.println("Original list: " + people);
        
        // Modify the original object
        original.setName("Jane");
        
        System.out.println("After modification: " + people);
    }
}

此示例演示了将 nCopies 与自定义 Person 类一起使用。 该列表包含对同一 Person 对象的多个引用。 更改原始对象会影响列表中的所有元素。

在使用可变对象时,理解这种行为很重要。 对于不可变对象(例如字符串),这种共享是安全的且节省内存。

将 nCopies 与 Streams 一起使用

Java 8 流可以与 nCopies 列表一起使用。 虽然该列表是不可变的,但我们可以使用流操作处理其元素。 此示例显示了过滤和映射操作。

NCopiesStreams.java
package com.zetcode;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class NCopiesStreams {

    public static void main(String[] args) {
        
        List<Integer> numbers = Collections.nCopies(10, 5);
        
        System.out.println("Original list: " + numbers);
        
        // Process with stream
        List<Integer> processed = numbers.stream()
            .map(n -> n * 2)          // Double each number
            .filter(n -> n > 5)        // Filter numbers > 5
            .collect(Collectors.toList());
            
        System.out.println("Processed list: " + processed);
    }
}

此示例使用 nCopies 创建一个由十个五组成的列表,然后使用流对其进行处理。 流操作将每个数字加倍,并过滤掉大于五的数字。 结果是一个新的可变列表。

Streams 提供了一种强大的方式来处理 nCopies 列表而无需修改它们。 这对于重复值的转换和计算很有用。

性能注意事项

nCopies 对于大型列表来说是非常节省内存的。 无论列表大小如何,它只存储一个元素引用。 此示例比较了 nCopies 和常规列表创建之间的内存使用情况。

PerformanceTest.java
package com.zetcode;

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

public class PerformanceTest {

    public static void main(String[] args) {
        final int SIZE = 1_000_000;
        
        // Using nCopies
        long start = System.nanoTime();
        List<String> nCopyList = Collections.nCopies(SIZE, "item");
        long nCopyTime = System.nanoTime() - start;
        
        // Using ArrayList
        start = System.nanoTime();
        List<String> arrayList = new ArrayList<>(SIZE);
        for (int i = 0; i < SIZE; i++) {
            arrayList.add("item");
        }
        long arrayListTime = System.nanoTime() - start;
        
        System.out.println("nCopies creation time (ms): " + 
            nCopyTime / 1_000_000);
        System.out.println("ArrayList creation time (ms): " + 
            arrayListTime / 1_000_000);
        System.out.println("nCopies memory efficient: " + 
            (nCopyList.size() == arrayList.size()));
    }
}

此示例演示了 nCopies 的性能优势。 使用 nCopies 创建一个包含一百万个元素的列表比手动填充 ArrayList 快得多。 它也更节省内存。

输出显示了两种方法之间的时间差异。 对于大型的、只读的相同元素列表,nCopies 是更好的选择。

数据结构中的 nCopies

nCopies 可用于初始化更复杂的数据结构。 此示例显示了创建列表的列表,其中每个子列表包含重复值。 这种模式对于矩阵初始化很有用。

MatrixInitialization.java
package com.zetcode;

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

public class MatrixInitialization {

    public static void main(String[] args) {
        
        final int ROWS = 3;
        final int COLS = 4;
        final int INIT_VALUE = 0;
        
        // Create list of lists using nCopies
        List<List<Integer>> matrix = new ArrayList<>(
            Collections.nCopies(ROWS, 
                new ArrayList<>(Collections.nCopies(COLS, INIT_VALUE))
        );
        
        System.out.println("Initial matrix:");
        matrix.forEach(row -> System.out.println(row));
        
        // Modify one element
        matrix.get(1).set(2, 99);
        
        System.out.println("\nModified matrix:");
        matrix.forEach(row -> System.out.println(row));
    }
}

此示例使用嵌套的 nCopies 创建一个用零初始化的 3x4 矩阵。 外部列表包含对同一内部列表的引用,因此修改一行会影响所有行。 这演示了 nCopies 的强大功能和潜在缺陷。

对于正确的矩阵初始化,您需要创建不同的内部列表。 此示例用作对嵌套结构中共享引用的警告。

nCopies 的替代方案

Java 8 引入了 Stream.generate 作为 nCopies 的替代方案。 此示例比较了创建具有重复元素的列表的两种方法。 每种方法都有不同的特性和用例。

StreamAlternative.java
package com.zetcode;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamAlternative {

    public static void main(String[] args) {
        
        // Using nCopies
        List<String> nCopyList = Collections.nCopies(5, "Java");
        System.out.println("nCopies result: " + nCopyList);
        
        // Using Stream.generate
        List<String> streamList = Stream.generate(() -> "Java")
            .limit(5)
            .collect(Collectors.toList());
        System.out.println("Stream result: " + streamList);
        
        // Performance comparison
        long start = System.nanoTime();
        Collections.nCopies(1_000_000, "Java");
        long nCopyTime = System.nanoTime() - start;
        
        start = System.nanoTime();
        Stream.generate(() -> "Java").limit(1_000_000).collect(Collectors.toList());
        long streamTime = System.nanoTime() - start;
        
        System.out.println("\nnCopies time (ms): " + nCopyTime / 1_000_000);
        System.out.println("Stream time (ms): " + streamTime / 1_000_000);
    }
}

此示例显示了创建具有重复元素的列表的两种方法。 nCopies 更有效地创建一个不可变列表,而 Stream.generate 创建一个更灵活的可变列表。

性能比较表明 nCopies 对于大型列表来说更快。 但是,如果您需要可变性或转换,流会提供更多处理选项。

来源

Java Collections.nCopies 文档

在本文中,我们深入探讨了 Collections.nCopies 方法。 我们涵盖了基本用法、性能特征和替代方案。 此方法对于创建重复元素的节省内存的不可变列表很有价值。

作者

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

列出所有Java教程