遍历TreeSet引发无限循环及自定义类TreeSet搜索问题求助
解决方案:TreeSet存储User并实现高效搜索(规避无限循环与禁用contains)
我来帮你一步步拆解这个需求,从核心实现到坑点规避都给你理清楚:
1. 先搞定User数据类(解决无限循环的核心)
TreeSet依赖元素的排序逻辑,错误的比较逻辑是导致遍历无限循环、元素存储异常的头号原因。因为我们是按邮箱搜索,所以必须以邮箱作为唯一比较依据,实现Comparable<User>接口:
public class User implements Comparable<User> { private final String email; private final String name; // 完整构造方法,用于CSV导入 public User(String email, String name) { this.email = email; this.name = name; } // 仅用于搜索的简化构造方法 public User(String email) { this(email, null); } // getter方法 public String getEmail() { return email; } public String getName() { return name; } // 核心:基于邮箱的一致比较逻辑 @Override public int compareTo(User other) { if (other == null) throw new NullPointerException(); // 按邮箱字典序比较,确保相同邮箱的User会被视为同一元素 return this.email.compareTo(other.getEmail()); } // 可选但推荐:保持equals/hashCode与compareTo逻辑一致 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return email.equals(user.email); } @Override public int hashCode() { return email.hashCode(); } }
划重点:如果
compareTo逻辑前后不一致(比如有时候比邮箱,有时候比姓名),TreeSet的内部红黑树结构会彻底混乱,遍历的时候大概率会陷入无限循环。
2. 导入CSV数据到TreeSet
用BufferedReader高效读取CSV,创建User实例加入TreeSet:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.TreeSet; public class UserSearchHandler { private TreeSet<User> userSet; public void loadUsersFromCsv(String csvPath) throws IOException { userSet = new TreeSet<>(); try (BufferedReader br = new BufferedReader(new FileReader(csvPath))) { String line; br.readLine(); // 跳过CSV表头(如果有的话) while ((line = br.readLine()) != null) { String[] parts = line.split(","); if (parts.length >= 2) { String email = parts[0].trim(); String name = parts[1].trim(); userSet.add(new User(email, name)); } } } } }
3. 实现禁用contains()的搜索逻辑
既然不能用contains(),我们可以利用TreeSet的导航方法(基于红黑树的有序性,搜索效率O(log n),比遍历快得多):
// 根据邮箱搜索User,返回null表示不存在 public User searchByEmail(String targetEmail) { User searchKey = new User(targetEmail); // ceiling方法返回大于等于searchKey的第一个元素 User candidate = userSet.ceiling(searchKey); // 验证候选元素的邮箱是否完全匹配 if (candidate != null && candidate.getEmail().equals(targetEmail)) { return candidate; } return null; }
如果一定要用遍历(不推荐,效率低),要注意绝对不能在遍历中修改TreeSet(add/remove元素),并且可以利用有序性提前终止:
// 遍历版搜索(仅作演示) public User searchByEmailIterative(String targetEmail) { for (User user : userSet) { if (user.getEmail().equals(targetEmail)) { return user; } // 因为TreeSet有序,当遍历到邮箱大于目标时,后面的都不用看了 if (user.getEmail().compareTo(targetEmail) > 0) { break; } } return null; }
4. 处理4000次重复搜索的性能优化
每次都读TXT文件会慢到离谱,先把所有目标邮箱缓存到内存,再重复搜索:
import java.util.List; import java.util.stream.Collectors; public List<String> loadTargetEmails(String txtPath) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(txtPath))) { return br.lines() .map(String::trim) .filter(line -> !line.isEmpty()) .collect(Collectors.toList()); } } // 执行4000次重复搜索 public void runRepeatedSearches(List<String> targetEmails) { for (int i = 0; i < 4000; i++) { for (String email : targetEmails) { User result = searchByEmail(email); // 这里可以添加结果处理逻辑,比如统计命中数 } } }
5. 彻底规避遍历无限循环的注意事项
- 遍历TreeSet时,绝对不要调用
add()/remove()修改集合(除非用迭代器的remove()方法),否则会破坏红黑树结构,导致遍历无法终止。 - 确保
User的compareTo逻辑全程一致,不能在不同场景切换比较维度。 - 不要用反射等手段修改TreeSet的内部结构,这会直接打乱有序性。
内容的提问来源于stack exchange,提问作者Connor Hobson
相关产品推荐
相关产品推荐

