共计 7401 个字符,预计需要花费 19 分钟才能阅读完成。
系列文章
「深入浅出」集合 List
「深入浅出」java 集合 Collection 和 Map
Set 继承自 Collection 接口,不能包含有重复元素。本篇文章主要讲 Set 中三个比较重要的实现类:HashSet、TreeSet。
Set
Set 是一个存储无序且不重复元素的集合。在使用 Set 集合的时候,应该注意两点
为 Set 集合里的元素的实现类重写 equals()和 hashCode()方法()
若传入重复的元素, 重复元素会被忽略(可以用于做集合的去重)
扩展判断两个元素相等的标准:两个对象通过 equals()方法比较相等,并且两个对象的 hashCode()方法返回值也相等。
HashSet
HashSet 是 Set 接口的典型实现, 是哈希表结构,主要利用 HashMap 的 key 来存储元素,计算插入元素的 hashCode 来获取元素在集合中的位置, 因此具有很好的存取和查找性能。
主要特点 1. 不允许出现重复元素 2. 存储的元素是无序的 3. 不是同步的,如果多个线程同时访问一个 HashSet,则必须通过代码来保证其同步。4. 集合元素值可以是 null。
HashSet 基本操作
public class HashSetTest {
public static void main(String[] agrs){
// 创建 HashSet 集合:
Set<String> hashSet = new HashSet<String>();
System.out.println(“HashSet 初始容量大小:”+hashSet.size());
// 元素添加:
hashSet.add(“my”);
hashSet.add(“name”);
hashSet.add(“is”);
hashSet.add(“ken”);
hashSet.add(“hello”);
hashSet.add(“everyone”);
System.out.println(“HashSet 容量大小:”+hashSet.size());
// 迭代器遍历:
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
String str = iterator.next();
System.out.print(str+” “);
}
System.out.print(“\n”);
// 元素删除:
hashSet.remove(“ken”);
System.out.println(“HashSet 元素大小:” + hashSet.size());
hashSet.clear();
System.out.println(“HashSet 元素大小:” + hashSet.size());
// 集合判断:
boolean isEmpty = hashSet.isEmpty();
System.out.println(“HashSet 是否为空:” + isEmpty);
boolean isContains = hashSet.contains(“hello”);
System.out.println(“HashSet 是否为空:” + isContains);
}
}
//out:
HashSet 初始容量大小:0
HashSet 容量大小:6
ken everyone name is hello my
HashSet 元素大小:5
HashSet 元素大小:0
HashSet 是否为空:true
HashSet 是否为空:false
细节注意事项可看以下例子:
// 重写类 A 的 equals 方法总是返回 true, 但没有重写其 hashCode()方法。
// 不能保证当前对象是 HashSet 中的唯一对象
class A {
@Override
public boolean equals(Object obj) {
return true;
}
}
// 重写类 B 的 hashCode()方法总是返回 1, 但没有重写其 equals()方法。
// 不能保证当前对象是 HashSet 中的唯一对象
class B {
@Override
public int hashCode() {
return 1;
}
}
// 重写重写类 C 的 hashCode()方法总是返回 2, 且有重写其 equals()方法
class C {
@Override
public int hashCode() {
return 2;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
public class HashSetTest {
public static void main(String[] args) {
HashSet books = new HashSet();
// 分别向 books 集合中添加两个 A 对象,两个 B 对象,两个 C 对象
books.add(new A());
books.add(new A());
books.add(new B());
books.add(new B());
books.add(new C());
books.add(new C());
System.out.println(books);
}
}
//out
[B@1, B@1, C@2, A@3bc257, A@785d65]
可以看出, 只有当同时重写了 equals 方法和 hashCode 方法时, 才能按照自己的意图判断对象是否相等, 否则均判定为不相等, 可加入 Set 集合中。
TreeSet
与 HashSet 集合类似,TreeSet 也是基于 Map 来实现,其底层结构为红黑树(特殊的二叉查找树)与 HashSet 不同的是,TreeSet 具有排序功能,分为自然排序 (123456) 和自定义排序两类,默认是自然排序
具有如下特点:
对插入的元素进行排序,是一个有序的集合(主要与 HashSet 的区别)
底层使用红黑树结构,而不是哈希表结构
允许插入 Null 值
不允许插入重复元素
线程不安全
TreeSet 基本操作
public class TreeSetTest {
public static void main(String[] agrs){
TreeSet<String> treeSet = new TreeSet<String>();
System.out.println(“TreeSet 初始化容量大小:”+treeSet.size());
// 元素添加:
treeSet.add(“my”);
treeSet.add(“name”);
treeSet.add(“ken”);
treeSet.add(“hello”);
treeSet.add(“world”);
treeSet.add(“1”);
treeSet.add(“2”);
treeSet.add(“3”);
System.out.println(“TreeSet 容量大小:” + treeSet.size());
System.out.println(“TreeSet 元素顺序为:” + treeSet.toString());
System.out.println(“ 遍历元素升序:”);
// 迭代器遍历:升序
Iterator<String> iteratorAesc = treeSet.iterator();
while(iteratorAesc.hasNext()){
String str = iteratorAesc.next();
System.out.print(str + ” “);
}
System.out.println(“\n”);
System.out.println(“ 遍历元素降序:”);
// 迭代器遍历:降序
Iterator<String> iteratorDesc = treeSet.descendingIterator();
while(iteratorDesc.hasNext()){
String str = iteratorDesc.next();
System.out.print(str + ” “);
}
System.out.println(“\n”);
// 元素获取: 实现 NavigableSet 接口
String firstEle = treeSet.first();// 获取 TreeSet 头节点:
System.out.println(“TreeSet 头节点为:” + firstEle);
// 获取指定元素之前的所有元素集合:(不包含指定元素)
SortedSet<String> headSet = treeSet.headSet(“ken”);
System.out.println(“ken 节点之前的元素为:”+headSet.toString());
// 获取给定元素之间的集合:(包含头,不包含尾)
SortedSet subSet = treeSet.subSet(“1″,”hello”);
System.out.println(“1–hello 之间节点元素为:”+subSet.toString());
// 集合判断:
boolean isEmpty = treeSet.isEmpty();
System.out.println(“TreeSet 是否为空:”+isEmpty);
boolean isContain = treeSet.contains(“who”);
System.out.println(“TreeSet 是否包含 who 元素:”+isContain);
// 元素删除:
boolean kenRemove = treeSet.remove(“ken”);
System.out.println(“ken 元素是否被删除 ”+kenRemove);
// 集合中不存在的元素,删除返回 false
boolean whoRemove = treeSet.remove(“who”);
System.out.println(“who 元素是否被删除 ”+whoRemove);
// 删除并返回第一个元素:如果 set 集合不存在元素,则返回 null
String pollFirst = treeSet.pollFirst();
System.out.println(“ 删除的第一个元素:”+pollFirst);
// 删除并返回最后一个元素:如果 set 集合不存在元素,则返回 null
String pollLast = treeSet.pollLast();
System.out.println(“ 删除的最后一个元素:”+pollLast);
treeSet.clear();// 清空集合
}
}
//out:
TreeSet 初始化容量大小:0
TreeSet 容量大小:8
TreeSet 元素顺序为:[1, 2, 3, hello, ken, my, name, world]
遍历元素升序:
1 2 3 hello ken my name world
遍历元素降序:
world name my ken hello 3 2 1
TreeSet 头节点为:1
ken 节点之前的元素为:[1, 2, 3, hello]
1–hello 之间节点元素为:[1, 2, 3]
TreeSet 是否为空:false
TreeSet 是否包含 who 元素:false
jiaboyan 元素是否被删除 false
who 元素是否被删除 false
删除的第一个元素:1
删除的最后一个元素:world
TreeSet 元素排序
我们讲到了 TreeSet 是一个有序集合,可以对集合元素排序,其中分为自然排序和自定义排序
自然排序(正序与反序)
public class TreeSetTest {
public static void main(String[] agrs){
TreeSet<String> treeSetString = new TreeSet<String>();
treeSetString.add(“a”);
treeSetString.add(“z”);
treeSetString.add(“d”);
treeSetString.add(“b”);
System.out.println(“ 字母正序:” + treeSetString.toString());
System.out.println(“ 字母反序:” + treeSetString.descendingSet().toString());
TreeSet<Integer> treeSetInteger = new TreeSet<Integer>();
treeSetInteger.add(1);
treeSetInteger.add(24);
treeSetInteger.add(23);
treeSetInteger.add(6);
System.out.println(“ 数字正序:” + treeSetInteger.toString());
System.out.println(“ 数字反序:” + treeSetInteger.descendingSet().toString());
}
}
//out
字母顺序:[a, b, d, z]
数字顺序:[1, 6, 23, 24]
自定义排序
当使用的是自己定义的类时, 就需要做一些特殊处理, 否则会报错 Exception in thread “main” java.lang.ClassCastException, 有两种实现方式 1. 实现 Comparable 接口
public class Person implements Comparable<Person>{
private String name;
private Integer age;
public Person(){}
public Person(String name,Integer age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public int compareTo(Person person) {
// 如果 name 长度一样,则比较年龄的大小
// 需要考虑相等的情况, 否则相等的情况只显示先加入集合的元素
if(this.age.equals(person.age)){
return 1;
}
return this.age – person.age;
}
@Override
public String toString() {
return “Person{” +
“name='” + name + ‘\” +
“, age=” + age +
‘}’;
}
public static void main(String[] agrs){
TreeSet<Person> treeSet = new TreeSet<Person>();
// 排序对象:
Person p1 = new Person(“ken”,18);
Person p2 = new Person(“xiaoming”,15);
Person p3 = new Person(“laowang”,15);
Person p4 = new Person(“zhangsan”,25);
// 添加到集合:
treeSet.add(p1);
treeSet.add(p2);
treeSet.add(p3);
treeSet.add(p4);
System.out.println(“TreeSet 集合顺序为:”+treeSet);
}
}
//out:
TreeSet 集合顺序为:[Person{name=’xiaoming’, age=15}, Person{name=’laowang’, age=15},
Person{name=’ken’, age=18}, Person{name=’zhangsan’, age=25}]
2. 实现 Comparetor<t style=”margin: 0px; padding: 0px; max-width: 100%; overflow-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;”> 接口,并重写 compare 方法 </t>
// 自定义 Person 类的比较器:
public class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
// 比较名字长度, 从大到小排序
// 需要考虑相等的情况, 否则相等的情况只显示先加入集合的元素
if(p2.getName().length() == p1.getName().length()){
return 1;
}
return p2.getName().length() – p1.getName().length();
}
}
测试程序
public static void main(String[] agrs){
TreeSet<Person> treeSet = new TreeSet<Person>(new PersonComparator());
// 排序对象:
Person p1 = new Person(“ken”,18);
Person p2 = new Person(“xiaoming”,15);
Person p3 = new Person(“laowang”,15);
Person p4 = new Person(“zhangsan”,25);
// 添加到集合:
treeSet.add(p1);
treeSet.add(p2);
treeSet.add(p3);
treeSet.add(p4);
System.out.println(“TreeSet 集合顺序为:”+treeSet);
}
//out
TreeSet 集合顺序为:[Person{name=’xiaoming’, age=15}, Person{name=’zhangsan’, age=25},
Person{name=’laowang’, age=15}, Person{name=’ken’, age=18}]
后续文章将对 java 集合中的具体实现类进行深入了解。有兴趣的话可以观看后续内容,进一步了解 java 集合内容。
推荐阅读: 个人网站模板推荐如何在 GitHub 上大显身手?IDEA 热部署插件 JRebel
坚持日更:7 天
您的点赞、转发是对我最大的支持!
THANDKS
End –
一个立志成大腿而每天努力奋斗的年轻人
伴学习伴成长,成长之路你并不孤单!