关于HashSet类,这是我刷leetcode时看了官方的一个代码写法产生的疑问,在网上没有搜寻到答案,最后在API找到的答案:
为什么在网上找不到答案呢?显示的都是说HasSet不能存储重复的元素(或许我的理解有问题),越看越迷糊,最后突然想到去看API才找到正解!----->> 地址 != 数值(内容),下面统一用”内容“代替我们常说的”数值“ !!!!
HashSet这里的重复值并不是指“内容相同”的两个变量,而是两个变量的“地址”是否指向同一块,地址相同的才是“重复值”。但是,在基本数据类型中 int,double,float 等基本类型,这些类型”相同的内容“会指向同一块地址,但是自定义的类所实例化(new)的不同对象会指向不同的地址,尽管“内容相同”,但在HashSet 类中不会被判为重复值,看下面代码更清晰
基本数据类型以 int 为例:
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc =new Scanner(System.in) ;
int a = 3 ;
int b = 3 ;
System.out.println("整数类型:");
System.out.println("a的地址: " + System.identityHashCode(a));
System.out.println("b的地址: " + System.identityHashCode(b));
Set<Integer> intSet = new HashSet<Integer>() ;
System.out.print("输入值:");
for(int i=0; i<5; i++){
intSet.add(sc.nextInt()) ;
}
Iterator<Integer> intIt = intSet.iterator() ;
while(intIt.hasNext()){
System.out.print(intIt.next() + " ");
}
sc.close();
}
}
运行结果:
int等基本数据类型的”相同内容“指向的地址相同,”相同内容“都被去掉了
String是属于引用类型,直接赋予”相同字符串“给不同的变量,它们指向的地址是相同的,但是用“相同内容”new String()创建的不同变量又会指向不同的地址,而自定义的类就像new String(),实例化(new)”相同内容“给不同变量,它们指向的地址是不同的。
总的来说,就是每new 一个对象都会为对应的变量开辟一个独有的地址
看下面代码示例:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
//自定义类
class ListNode {
int val;
ListNode(int x) {
val = x;
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc =new Scanner(System.in) ;
//new String类型
System.out.println("new String类型:");
String sk = new String("kkk") ;
String sg = new String("kkk") ;
System.out.println("sk的地址: " + System.identityHashCode(sk));
System.out.println("sg的地址: " + System.identityHashCode(sg));
//直接赋值字符串
String s1 ="kkk" ;
String s2 ="kkk" ;
System.out.println("String类型:");
//分别 输出变量s1,s2 地址
System.out.println("s1的地址: " + System.identityHashCode(s1));
System.out.println("s2的地址: " + System.identityHashCode(s2));
//建立HashSet表
Set<String> strSet = new HashSet<String>() ;
//输入值验证
for(int i=0; i<5; i++){
strSet.add(sc.next()) ;
}
//HashSet的迭代器---输出
Iterator<String> str = strSet.iterator() ;
while(str.hasNext()){
System.out.print(str.next() + " ");
}
System.out.println();
System.out.println("-----------------------------------------");
System.out.println();
// 自定义类ListNode
ListNode n1 = new ListNode(1) ;
ListNode n2 = new ListNode(1) ;
System.out.println("自定义类ListNode:");
//输出n1,n2 地址
System.out.println("n1= " + n1 + " n1的地址: " + System.identityHashCode(n1));
System.out.println("n2= " + n2 + " n2的地址: " + System.identityHashCode(n2));
//创建HashSet表
Set<ListNode> nSet = new HashSet<ListNode>() ;
//随便输入几个值验证
for(int i=0; i<5; i++){
nSet.add(new ListNode(sc.nextInt())) ;
}
//HashSet的迭代器---输出
Iterator<ListNode> nd = nSet.iterator() ;
while(nd.hasNext()){
System.out.print(nd.next().val + " ");
}
sc.close();
}
}
运行结果:
通过运行的结果对比,想必很直观了,“相同内容”的new String()的不同变量指向的地址不同,这里就没用列表演示了(类似下面的ListNode类),可以自行去试一下。而String的直接赋值”相同内容“会指向同一块地址,所以会被去掉,而自定义的ListNode类,尽管”内容相同“,但它们指向的地址不同,所以不会被当成”重复内容“去掉。
另外,HashSet类不保证元素的顺序,如果要求元素顺序的,这个就不适用了
TreeSet类就比较直接了,它所指的重复值就是”相同内容“,与数据类型、地址无关
下面代码示例:
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
//自定义类需要实现Comparable<>接口,才能使用TreeSet类,否则运行会报错
class ListNode implements Comparable<ListNode>{
int val;
ListNode(int x) {
val = x;
}
@Override
public int compareTo(ListNode arg) {
// TODO Auto-generated method stub
if(this.val > arg.val)
return 1 ;
else if(this.val < arg.val)
return -1 ;
else
return 0;
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc =new Scanner(System.in) ;
//实现int型TreeSet类
Set<Integer> intSet = new TreeSet<Integer>() ;
System.out.println("int型:");
for(int i=0; i<6; i++){
intSet.add(sc.nextInt()) ;
}
for(int y: intSet){
System.out.print(y + " ");
}
System.out.println();
System.out.println();
System.out.println();
//实现自定义类型TreeSet类
Set<ListNode> nSet = new TreeSet<ListNode>() ;
System.out.println("自定义类型:");
for(int j=0; j<6; j++){
nSet.add(new ListNode(sc.nextInt())) ;
}
for(ListNode x: nSet){
System.out.print(x.val + " ");
}
sc.close();
}
}
运行结果:
从运行结果可知,TreeSet类会去掉“相同的内容”,也就是我们平常说的“数值”,最后升序输出