Java集合框架(三)Set
Java集合框架(三)Set
Set子接口
- 特点:无序、无下标、元素不可重复
- 方法:全部继承自Collection中的方法
Set的实现类
-
Set的实现类包括HashSet、TreeSet
-
举例1:
//Set特点:无序没有下标、不能重复 public class Demo01 { public static void main(String[] args) { //创建集合 //Set是接口 Set<String> set = new HashSet<>(); set.add("苹果"); set.add("大米"); set.add("小米"); set.add("香蕉"); System.out.println(set.toString()); } } //输出(输出顺序与存入顺序是不同的,代表无序): [苹果, 香蕉, 大米, 小米]
-
举例2:
//Set特点:无序没有下标、不能重复 public class Demo01 { public static void main(String[] args) { //创建集合 //Set是接口 Set<String> set = new HashSet<>(); set.add("苹果"); set.add("大米"); set.add("小米"); set.add("香蕉"); System.out.println("--------输出当前数据-----------"); System.out.println(set.toString()); System.out.println("----------删除大米----------"); set.remove("大米"); System.out.println(set.toString()); System.out.println("-------遍历1(foreach)--------"); for (String s : set) { System.out.println(s); } System.out.println("-------遍历2(迭代器)--------"); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("-------判断是否有小米--------"); System.out.println(set.contains("小米")); System.out.println("-------判断是否为空--------"); System.out.println(set.isEmpty()); } } //输出: --------输出当前数据----------- [苹果, 香蕉, 大米, 小米] ----------删除大米---------- [苹果, 香蕉, 小米] -------遍历1(foreach)-------- 苹果 香蕉 小米 -------遍历2(迭代器)-------- 苹果 香蕉 小米 -------判断是否有小米-------- true -------判断是否为空-------- false
HashSet
-
基于HashCode实现元素不重复
-
当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入(元素不能重复)
-
HashSet的存储方法:
- 根据哈希计算存储的位置是否为空,若为空则直接存储,若不为空则进行第二部
- 通过equals判断是否为重复值,如果为true则认为重复,否则,形成链表
-
可以通过修改hascode方法来存储相同的元素,让hascode在相同的元素每次存储加上一个数,修改存储位置,避免equals导致重复数据无法存入
-
类中可以重写equals方法,通过重写equals来改变判断的方式
-
重写equals()必须要先重写hasCode()方法,因为创建两个不同对象但内容相同的元素时,他们默认的hashCode()会让两个对象的下标位置不同,进而当作两个不同的对象存储进去。
只有修改为通过内容获取哈希值,才能让相同内容的不同对象存在同一下标的链表上,之后才会有equals判断内容是否相等。(举例3)
-
举例1:
public class Demo02 { public static void main(String[] args) { HashSet<String> s1 = new HashSet<>(); s1.add("香蕉"); s1.add("苹果"); s1.add("葡萄"); s1.add("棒球"); System.out.println("---------输出结果---------"); System.out.println("元素个数:"+s1.size()); System.out.println(s1.toString()); System.out.println("--------删除----------"); s1.remove("葡萄"); System.out.println("--------输出结果----------"); System.out.println("元素个数:"+s1.size()); System.out.println(s1.toString()); System.out.println("--------foreach输出----------"); for (String s : s1) { System.out.println(s); } System.out.println("--------迭代输出----------"); Iterator iterator = s1.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("--------判断----------"); System.out.println("是否为空:"+s1.isEmpty()); System.out.println("是否有棒球:"+s1.contains("棒球")); } } //输出: ---------输出结果--------- 元素个数:4 [香蕉, 苹果, 葡萄, 棒球] --------删除---------- --------输出结果---------- 元素个数:3 [香蕉, 苹果, 棒球] --------foreach输出---------- 香蕉 苹果 棒球 --------迭代输出---------- 香蕉 苹果 棒球 --------判断---------- 是否为空:false 是否有棒球:true
-
举例2:
类:
public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name,int age) { this.name = name; this.age = age; } public String toString(){ return this.name+" "+this.age; } }
主函数:
public class Demo03 { public static void main(String[] args) { HashSet<Person> hashSet = new HashSet<>(); Person p1 = new Person("张三",18); Person p2 = new Person("李四",19); Person p3 = new Person("王五",20); Person p4 = new Person("赵六",16); Person p5 = new Person("李四",19); hashSet.add(p1); hashSet.add(p2); hashSet.add(p3); hashSet.add(p4); hashSet.add(p4);//重复 hashSet.add(p5); //hashSet不会重复存储相同的数据 //重复指的是存储的哈希值相同,但相同数据内容的不同对象的哈希值不同(因为存储位置不同) //相同数据内容的不同对象的不算重复数据(例如p5和p2) System.out.println("元素个数:"+hashSet.size()); System.out.println(hashSet.toString()); System.out.println("--------删除----------"); hashSet.remove(p3);//根据哈希值来找,删除 System.out.println("--------输出结果----------"); System.out.println("元素个数:"+hashSet.size()); System.out.println(hashSet.toString()); //因为重写了toString()方法,所以foreach能正常输出(而不是输出哈希值) System.out.println("--------foreach输出----------"); for (Person person : hashSet) { System.out.println(person.toString()); } //因为重写了toString()方法,所以迭代能正常输出(而不是输出哈希值) System.out.println("--------迭代输出----------"); Iterator iterator = hashSet.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("--------判断----------"); System.out.println("是否为空:"+hashSet.isEmpty()); System.out.println("是否有棒球:"+hashSet.contains("棒球")); } } //输出: 元素个数:5 [王五 20, 李四 19, 李四 19, 赵六 16, 张三 18] --------删除---------- --------输出结果---------- 元素个数:4 [李四 19, 李四 19, 赵六 16, 张三 18] --------foreach输出---------- 李四 19 李四 19 赵六 16 张三 18 --------迭代输出---------- 李四 19 李四 19 赵六 16 张三 18 --------判断---------- 是否为空:false 是否有棒球:false
-
举例3(重写equals):
类(重写equals和hasCode):
public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name,int age) { this.name = name; this.age = age; } public String toString(){ return this.name+" "+this.age; } /* 重写equals()必须要先重写hasCode()方法: 因为创建两个不同对象但内容相同的元素时,他们默认的hashCode()会让两个对象的下标位置不同,进而当作两个不同的对象存储进去。 只有修改为通过内容获取哈希值,才能让相同内容的不同对象存在同一下标的链表上,之后才会有equals判断内容是否相等。 */ @Override public int hashCode() { //根据内容得出哈希值 //存储时如果哈希值相同,则存到相同下标的链表上 return Objects.hash(name, age); } //重写equals public boolean equals(Object obj){ //在同一下标的链表上进行比较 //this代表调用这个方法的对象的地址(this.equals(Object obj)) //obj代表这个方法中参数传递过来对象的地址(直接用最大的Object来全覆盖) //如果地址相同,则数据肯定相同,不用再往后比较了,直接返回true if(this==obj){ return true; } //判断是否为空,为空则返回false if(obj==null){ return false; } //若地址不同,则判断其中的数据内容是否相同 //若数据内容相同则返回true //obj instanceof Person判断obj是否为Person的一个实例,若是,则再比较内容 if(obj instanceof Person){ //equals本来是比较地址的,但String重写了equals方法,所以String的equals比较就是在比较值的内容 Person p = (Person)obj; if(this.name.equals(p.getName()) && this.age==p.getAge()){ return true; } } //若内容不同,则返回为false return false; } }
主函数:
public class Demo04 { public static void main(String[] args) { HashSet<Person> hashSet2 = new HashSet<>(); Person p1 = new Person("张三",18); Person p2 = new Person("李四",19); Person p3 = new Person("王五",20); Person p4 = new Person("赵六",16); Person p5 = new Person("李四",19); hashSet2.add(p1); hashSet2.add(p2); hashSet2.add(p3); hashSet2.add(p4); hashSet2.add(p4);//重复 hashSet2.add(p5); //由于equals已经重写,所以凡是内容相同或者地址相同的都不能重复存储 System.out.println("元素个数:"+hashSet2.size()); System.out.println(hashSet2.toString()); //删除 //由于equals和hasCode已经重写,所以可以不用地址,可以通过内容是否相同来删除 //由于equals和hasCode已经重写,所以下面的删除方式可以用(通过new Person的内容来寻找删除) System.out.println("--------删除----------"); hashSet2.remove(new Person("李四",19)); System.out.println("--------输出结果----------"); System.out.println("元素个数:"+hashSet2.size()); System.out.println(hashSet2.toString()); } } //输出: 元素个数:4 [张三 18, 赵六 16, 王五 20, 李四 19] --------删除---------- --------输出结果---------- 元素个数:3 [张三 18, 赵六 16, 王五 20]
TreeSet
-
采用二叉树的方式实现的
-
基于排列顺序实现元素不重复
-
TreeSet通过查找树存储(左小右大),但当一个数据结构里的属性不只一个时,计算机就不知道拿什么去比较了,这时候就自己告诉计算机按照什么去 比较大小(举例2)
-
方式1:实现Comparable接口(举例2)
-
方式2:通过Comparator实现定制比较(比较器)(举例3)
-
-
举例1:
public class Demo05 { public static void main(String[] args) { TreeSet<String> treeSet = new TreeSet<>(); treeSet.add("aa"); treeSet.add("bb"); treeSet.add("cc"); treeSet.add("dd"); System.out.println("---------输出------------"); System.out.println("元素个数"+treeSet.size()); System.out.println(treeSet.toString()); System.out.println("---------删除------------"); treeSet.remove("bb"); System.out.println("元素个数"+treeSet.size()); System.out.println(treeSet.toString()); System.out.println("---------遍历------------"); System.out.println("---------foreach------------"); for (String s : treeSet) { System.out.println(s); } System.out.println("---------迭代------------"); Iterator iterator = treeSet.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } } //输出: ---------输出------------ 元素个数4 [aa, bb, cc, dd] ---------删除------------ 元素个数3 [aa, cc, dd] ---------遍历------------ ---------foreach------------ aa cc dd ---------迭代------------ aa cc dd
-
举例2:
类(重写Comparable接口里的compareTo方法):
public class Person implements Comparable<Person>{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name,int age) { this.name = name; this.age = age; } public String toString(){ return this.name+" "+this.age; } /* 重写equals()必须要先重写hasCode()方法: 因为创建两个不同对象但内容相同的元素时,他们默认的hashCode()会让两个对象的下标位置不同,进而当作两个不同的对象存储进去。 只有修改为通过内容获取哈希值,才能让相同内容的不同对象存在同一下标的链表上,之后才会有equals判断内容是否相等。 */ @Override public int hashCode() { //根据内容得出哈希值 //存储时如果哈希值相同,则存到相同下标的链表上 return Objects.hash(name, age); } //重写equals public boolean equals(Object obj){ //在同一下标的链表上进行比较 //this代表调用这个方法的对象的地址(this.equals(Object obj)) //obj代表这个方法中参数传递过来对象的地址(直接用最大的Object来全覆盖) //如果地址相同,则数据肯定相同,不用再往后比较了,直接返回true if(this==obj){ return true; } //判断是否为空,为空则返回false if(obj==null){ return false; } //若地址不同,则判断其中的数据内容是否相同 //若数据内容相同则返回true //obj instanceof Person判断obj是否为Person的一个实例,若是,则再比较内容 if(obj instanceof Person){ //equals本来是比较地址的,但String重写了equals方法,所以String的equals比较就是在比较值的内容 Person p = (Person)obj; if(this.name.equals(p.getName()) && this.age==p.getAge()){ return true; } } //若内容不同,则返回为false return false; } //实现接口中compareTo的方法 //原compareTo用来进行比较大小,但因为属性过多计算机不知道比较什么所以要人为重写 //返回值为0代表重复元素 @Override public int compareTo(Person o) { //通过原来的compareTo方法比较是不是同一个人名(同一个人名则返回0) int n1=this.getName().compareTo(o.getName()); //计算年龄差 int n2=this.age-o.age; //如果名字不相同则返回名字比较的大小结果,否则返回年龄的比较大小结果 //如果n1等于0,则名字相同,这时比较年龄大小(返回年龄差),否则就返回名字比较结果 //如果n1和n2都为0,则代表是重复元素,在添加时就不能被添加 return n1==0?n2:n1; } }
主函数:
public class Demo06 { public static void main(String[] args) { TreeSet<Person> treeSet = new TreeSet<>(); Person p1 = new Person("张三",18); Person p2 = new Person("李四",19); Person p3 = new Person("王五",20); Person p4 = new Person("赵六",16); treeSet.add(p1); treeSet.add(p2); treeSet.add(p3); treeSet.add(p4); System.out.println("元素个数"+treeSet.size()); System.out.println(treeSet.toString()); System.out.println("---------删除1------------"); treeSet.remove(new Person("李四",19)); System.out.println("元素个数"+treeSet.size()); System.out.println(treeSet.toString()); System.out.println("---------删除2------------"); treeSet.remove(p3); System.out.println("元素个数"+treeSet.size()); System.out.println(treeSet.toString()); } } //输出: 元素个数4 [张三 18, 李四 19, 王五 20, 赵六 16] ---------删除1------------ 元素个数3 [张三 18, 王五 20, 赵六 16] ---------删除2------------ 元素个数2 [张三 18, 赵六 16]
-
举例3:
类:
public class Person2 { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person2(String name,int age) { this.name = name; this.age = age; } public String toString(){ return this.name+" "+this.age; } }
主函数:
public class Demo07 { public static void main(String[] args) { //使用匿名内部类 TreeSet<Person2> treeSet = new TreeSet<>(new Comparator<Person2>() { @Override public int compare(Person2 o1, Person2 o2) { int n1=o1.getName().compareTo(o2.getName()); //计算年龄差 int n2=o1.getAge()-o2.getAge(); //如果名字不相同则返回名字比较的大小结果,否则返回年龄的比较大小结果 //如果n1等于0,则名字相同,这时比较年龄大小(返回年龄差),否则就返回名字比较结果 //如果n1和n2都为0,则代表是重复元素,在添加时就不能被添加 return n1==0?n2:n1; } }); Person2 p1 = new Person2("张三",18); Person2 p2 = new Person2("李四",19); Person2 p3 = new Person2("王五",20); Person2 p4 = new Person2("赵六",16); treeSet.add(p1); treeSet.add(p2); treeSet.add(p3); treeSet.add(p4); System.out.println("元素个数"+treeSet.size()); System.out.println(treeSet.toString()); System.out.println("---------删除1------------"); treeSet.remove(new Person2("李四",19)); System.out.println("元素个数"+treeSet.size()); System.out.println(treeSet.toString()); } } //输出: 元素个数4 [张三 18, 李四 19, 王五 20, 赵六 16] ---------删除1------------ 元素个数3 [张三 18, 王五 20, 赵六 16]
通过TreeSet实现字符串按长度进行排序
//实现从小到大
public class Demo08 {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int n1=o1.length()-o2.length();
int n2=o1.compareTo(o2);
//先看长度是否相同,如果长度相同在通过compareTo比较
return n1==0?n2:n1;
}
});
treeSet.add("helloword");
treeSet.add("pingguo");
treeSet.add("zhansan");
treeSet.add("lisi");
treeSet.add("cat");
treeSet.add("dog");
System.out.println(treeSet.toString());
}
}
//输出:
[cat, dog, lisi, pingguo, zhansan, helloword]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!