java 中的 set 是 collection 接口的子接口,不允许存储重复元素,且不保证顺序(除特定实现外)。以下是核心特性和常用方法:
一、核心特性
| 特性 | 说明 |
|---|---|
| 唯一性 | 不允许重复元素(基于 equals() 和 hashcode() 判断) |
| 无序性 | 不保证插入顺序(linkedhashset 和 treeset 除外) |
| 允许 null | 通常允许一个 null(treeset 不允许) |
| 无索引 | 没有 get(index) 方法,不能通过索引访问 |
二、主要实现类对比
| 实现类 | 底层结构 | 有序性 | 线程安全 | 适用场景 |
|---|---|---|---|---|
| hashset | 哈希表(hashmap) | 无序 | 否 | 快速查找、去重 |
| linkedhashset | 哈希表 + 双向链表 | 插入顺序 | 否 | 需保持插入顺序 |
| treeset | 红黑树(treemap) | 自然/定制排序 | 否 | 需排序范围查询 |
| copyonwritearrayset | 数组 | 无序 | 是 | 读多写少并发场景 |
三、常用方法
1. 基础操作(继承自 collection)
set<string> set = new hashset<>();
// 增
set.add("a"); // 添加成功返回 true,已存在返回 false
set.addall(anotherset); // 批量添加
// 删
set.remove("a"); // 删除指定元素
set.removeif(s -> s.startswith("a")); // 按条件删除(java 8+)
set.clear(); // 清空所有元素
// 查
set.contains("a"); // 判断是否包含,o(1) 时间
set.isempty(); // 是否为空
set.size(); // 元素个数
// 遍历
for (string s : set) { }
set.foreach(system.out::println);
iterator<string> it = set.iterator();2. 批量操作(集合运算)
set<integer> set1 = new hashset<>(arrays.aslist(1, 2, 3)); set<integer> set2 = new hashset<>(arrays.aslist(2, 3, 4)); // 交集 set1.retainall(set2); // set1 变为 [2, 3] // 并集 set1.addall(set2); // set1 变为 [1, 2, 3, 4] // 差集 set1.removeall(set2); // set1 中去掉 set2 的元素 // 包含判断 set1.containsall(set2); // set1 是否包含 set2 所有元素
四、各实现类特性详解
1. hashset(最常用)
set<string> set = new hashset<>(); // 初始容量16,负载因子0.75 // 自定义初始容量(减少扩容次数) set<string> set = new hashset<>(1000); // 去重原理:先比较 hashcode,再比较 equals
⚠️ 注意:元素必须正确重写 hashcode() 和 equals()
// 错误示例:自定义类未重写导致去重失败
class person {
string name;
// 未重写 hashcode/equals,默认按地址比较
}
// 正确做法
class person {
string name;
@override
public boolean equals(object o) {
if (this == o) return true;
if (o == null || getclass() != o.getclass()) return false;
person person = (person) o;
return objects.equals(name, person.name);
}
@override
public int hashcode() {
return objects.hash(name);
}
}2. linkedhashset(保持插入顺序)
set<string> set = new linkedhashset<>();
set.add("b");
set.add("a");
set.add("c");
// 遍历顺序:b, a, c(与插入顺序一致)3. treeset(自动排序)
// 自然排序(元素实现 comparable) set<integer> set = new treeset<>(); set.add(3); set.add(1); set.add(2); // 遍历结果:1, 2, 3 // 定制排序(comparator) set<string> set = new treeset<>(comparator.reverseorder()); set<person> set = new treeset<>(comparator.comparing(person::getage)); // 特有方法(sortedset 接口) treeset<integer> ts = new treeset<>(); ts.first(); // 最小元素 ts.last(); // 最大元素 ts.headset(5); // 小于5的子集 ts.tailset(5); // 大于等于5的子集 ts.subset(2, 5); // [2, 5) 范围的子集
五、stream api 操作(java 8+)
list<integer> list = arrays.aslist(1, 2, 2, 3, 3, 3);
// list 转 set 去重
set<integer> set = list.stream().collect(collectors.toset());
// 过滤 + 收集
set<string> result = set.stream()
.filter(s -> s.length() > 3)
.map(string::touppercase)
.collect(collectors.tocollection(linkedhashset::new));
// 找最大/最小
optional<integer> max = set.stream().max(integer::compareto);六、线程安全方案
// 1. collections 包装(读写都加锁,性能差) set<string> syncset = collections.synchronizedset(new hashset<>()); // 2. copyonwritearrayset(读多写少推荐) set<string> cowset = new copyonwritearrayset<>(); // 写操作复制整个数组,读操作无锁 // 3. concurrenthashmap 的 keyset(java 8+) set<string> concurrentset = concurrenthashmap.newkeyset();
七、常见面试题
| 问题 | 答案 |
|---|---|
hashset 如何检查重复? | 先比较 hashcode,相同再比较 equals |
为什么重写 equals 必须重写 hashcode? | 保证相等对象有相同哈希值,否则 hashset 会去重失败 |
treeset 插入 null 报错? | 因为需要比较排序,null 无法比较 |
hashset 和 hashmap 关系? | hashset 底层是 hashmap,元素作为 key,value 是固定对象 |
到此这篇关于java中set特性与常用方法的文章就介绍到这了,更多相关java set方法内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论