介绍
在这个实验中,你将学习如何高效地检查 Java List 中是否包含重复元素。我们将探索一种使用 HashSet 数据结构的常见且有效的技术。
你将首先实现一个方法,该方法利用 HashSet 的元素唯一性特性,通过遍历列表并将元素添加到集合中来检测重复项。如果某个元素已经存在于集合中,则表示找到了重复项。随后,你将学习另一种方法,即比较原始列表的大小与用列表元素填充的 HashSet 的大小。最后,你将使用各种场景(包括空列表和 null 列表)来测试你的实现,以确保其健壮性。
使用 HashSet 进行重复元素检测
在这一步中,我们将探讨如何在 Java 中使用 HashSet 来高效地检测集合中的重复元素。HashSet 是 Java 集合框架(Java Collections Framework)的一部分,特别适用于存储唯一元素。
首先,在你的 ~/project 目录下创建一个名为 DuplicateDetector.java 的新 Java 文件。你可以使用左侧 WebIDE 的文件资源管理器来完成此操作。在 ~/project 区域右键单击,选择“新建文件”,然后输入 DuplicateDetector.java。
现在,在代码编辑器中打开 DuplicateDetector.java 文件,并添加以下代码:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DuplicateDetector {
public static boolean containsDuplicates(List<String> list) {
// Create a HashSet to store unique elements
Set<String> uniqueElements = new HashSet<>();
// Iterate through the list
for (String element : list) {
// If the element is already in the HashSet, it's a duplicate
if (uniqueElements.contains(element)) {
return true; // Found a duplicate
}
// Otherwise, add the element to the HashSet
uniqueElements.add(element);
}
// If the loop finishes without finding duplicates, return false
return false;
}
public static void main(String[] args) {
// Example usage
List<String> myListWithDuplicates = new ArrayList<>();
myListWithDuplicates.add("apple");
myListWithDuplicates.add("banana");
myListWithDuplicates.add("apple"); // Duplicate
myListWithDuplicates.add("orange");
List<String> myListWithoutDuplicates = new ArrayList<>();
myListWithoutDuplicates.add("grape");
myListWithoutDuplicates.add("mango");
myListWithoutDuplicates.add("kiwi");
System.out.println("List with duplicates: " + myListWithDuplicates);
System.out.println("Contains duplicates? " + containsDuplicates(myListWithDuplicates)); // Expected: true
System.out.println("\nList without duplicates: " + myListWithoutDuplicates);
System.out.println("Contains duplicates? " + containsDuplicates(myListWithoutDuplicates)); // Expected: false
}
}
让我们来理解这段代码的关键部分:
import java.util.ArrayList;、import java.util.HashSet;、import java.util.List;、import java.util.Set;:这些行从 Java 集合框架中导入了必要的类。public static boolean containsDuplicates(List<String> list):这是一个方法,它接受一个String对象的List作为输入,如果列表包含重复项,则返回true,否则返回false。Set<String> uniqueElements = new HashSet<>();:这行代码创建了一个名为uniqueElements的空HashSet。HashSet被设计为只存储唯一元素。for (String element : list):这个循环遍历输入list中的每个element。if (uniqueElements.contains(element)):这行代码检查当前element是否已经存在于uniqueElementsHashSet中。如果存在,意味着我们找到了一个重复项,该方法返回true。uniqueElements.add(element);:如果元素还不在HashSet中,则将其添加进去。由于HashSet只存储唯一元素,添加已经存在的元素不会产生任何效果。return false;:如果循环结束后没有找到任何重复项,该方法返回false。main方法展示了如何使用containsDuplicates方法处理示例列表。
保存 DuplicateDetector.java 文件(Ctrl+S 或 Cmd+S)。
现在,让我们在终端中编译并运行这个程序。确保你位于 ~/project 目录下。
编译代码:
javac DuplicateDetector.java
如果没有编译错误,你将看不到任何输出。
现在,运行编译后的代码:
java DuplicateDetector
你应该会看到类似于以下的输出:
List with duplicates: [apple, banana, apple, orange]
Contains duplicates? true
List without duplicates: [grape, mango, kiwi]
Contains duplicates? false
这个输出证实了我们的 containsDuplicates 方法正确地识别出了包含重复项的列表。使用 HashSet 是一种高效的检查重复项的方法,因为平均而言,在 HashSet 中检查元素是否存在(使用 contains() 方法)的速度非常快。
比较列表大小和集合大小
在上一步中,我们通过遍历列表并将元素添加到 HashSet 中来检查重复项。一种更简单且通常更高效的检测重复项的方法是比较原始列表的大小和从该列表创建的 HashSet 的大小。
请记住,HashSet 只存储唯一元素。如果列表包含重复项,那么从该列表创建的 HashSet 的大小将小于原始列表的大小。如果没有重复项,两者的大小将相同。
让我们修改 DuplicateDetector.java 文件来实现这种方法。在代码编辑器中打开 ~/project/DuplicateDetector.java。
将 containsDuplicates 方法替换为以下代码:
public static boolean containsDuplicates(List<String> list) {
// Create a HashSet from the list
Set<String> uniqueElements = new HashSet<>(list);
// Compare the size of the list with the size of the HashSet
return list.size() != uniqueElements.size();
}
以下是新代码的工作原理:
Set<String> uniqueElements = new HashSet<>(list);:这行代码直接创建一个HashSet,并使用输入list中的所有元素对其进行初始化。HashSet会自动处理唯一性,因此列表中的任何重复元素都不会被添加到集合中。return list.size() != uniqueElements.size();:这行代码比较原始list中的元素数量(list.size())和HashSet中的唯一元素数量(uniqueElements.size())。如果大小不同(!=),则意味着列表中存在重复项,该方法返回true。如果大小相同,则表示没有重复项,该方法返回false。
main 方法可以保持不变,因为它已经调用了 containsDuplicates 方法。
保存 DuplicateDetector.java 文件(Ctrl+S 或 Cmd+S)。
现在,让我们编译并运行修改后的程序。确保你在终端中位于 ~/project 目录下。
编译代码:
javac DuplicateDetector.java
运行编译后的代码:
java DuplicateDetector
你应该会看到与之前相同的输出:
List with duplicates: [apple, banana, apple, orange]
Contains duplicates? true
List without duplicates: [grape, mango, kiwi]
Contains duplicates? false
这证实了我们使用大小比较来检测重复项的新的、更简单的方法是正确的。这种方法通常更简洁,并且在处理较大列表时,通常比逐个迭代和检查元素是否存在更高效。
使用空列表和 null 列表进行测试
在实际编程中,考虑边界情况非常重要,例如列表为空甚至为 null 的情况。我们当前的 containsDuplicates 方法对于包含元素的列表效果很好,但如果传入一个空列表或 null 列表会发生什么呢?
让我们通过在 ~/project/DuplicateDetector.java 的 main 方法中添加更多示例来测试这一点。在代码编辑器中打开该文件,并在现有代码之后的 main 方法中添加以下行:
System.out.println("\nEmpty list: " + new ArrayList<>());
System.out.println("Contains duplicates? " + containsDuplicates(new ArrayList<>())); // Expected: false
List<String> nullList = null;
System.out.println("\nNull list: " + nullList);
// The following line will cause a NullPointerException if not handled
// System.out.println("Contains duplicates? " + containsDuplicates(nullList));
保存文件(Ctrl+S 或 Cmd+S)。
现在,再次编译并运行程序。
编译:
javac DuplicateDetector.java
运行:
java DuplicateDetector
你应该会看到空列表的输出:
List with duplicates: [apple, banana, apple, orange]
Contains duplicates? true
List without duplicates: [grape, mango, kiwi]
Contains duplicates? false
Empty list: []
Contains duplicates? false
空列表的输出是正确的;空列表不包含重复项。
然而,如果你取消注释行 System.out.println("Contains duplicates? " + containsDuplicates(nullList)); 并尝试编译和运行,你将得到一个 NullPointerException。这是因为我们试图从一个 null 列表创建一个 HashSet,这是不允许的。
为了让我们的 containsDuplicates 方法更健壮,我们应该处理输入列表为 null 的情况。我们可以在方法开始处添加一个检查。
修改 ~/project/DuplicateDetector.java 中的 containsDuplicates 方法,以包含空值检查:
public static boolean containsDuplicates(List<String> list) {
// Handle null input
if (list == null) {
return false; // A null list does not contain duplicates
}
// Create a HashSet from the list
Set<String> uniqueElements = new HashSet<>(list);
// Compare the size of the list with the size of the HashSet
return list.size() != uniqueElements.size();
}
现在,取消注释 main 方法中测试 null 列表的行:
List<String> nullList = null;
System.out.println("\nNull list: " + nullList);
System.out.println("Contains duplicates? " + containsDuplicates(nullList)); // Expected: false
保存文件(Ctrl+S 或 Cmd+S)。
最后一次编译并运行程序。
编译:
javac DuplicateDetector.java
运行:
java DuplicateDetector
现在输出应该包含 null 列表的结果,并且不会崩溃:
List with duplicates: [apple, banana, apple, orange]
Contains duplicates? true
List without duplicates: [grape, mango, kiwi]
Contains duplicates? false
Empty list: []
Contains duplicates? false
Null list: null
Contains duplicates? false
通过添加空值检查,我们的 containsDuplicates 方法现在更健壮了,可以优雅地处理 null 输入。这是编程中防止意外错误的重要实践。
总结
在本次实验中,我们学习了如何检查 Java List 是否包含重复元素。我们探索了使用 HashSet 来高效检测重复项的方法。通过遍历列表并尝试将每个元素添加到 HashSet 中,我们可以快速判断某个元素是否已经存在,从而确定是否有重复项。
我们还学习了另一种方法,即比较原始列表的大小和从该列表创建的 HashSet 的大小。如果大小不同,则表明存在重复项。最后,我们通过使用空列表和 null 列表对这些方法进行测试,考虑了边界情况,以确保方法的健壮性。



