set集合HashSet

一:

和List接口同一级的还有Set接口,Set类型的集合,元素不能重复,存储顺序和迭代顺序没有必然联系。他的元素的唯一性是由hasCode和equals决定的。

他的子类,常用的HashSet和LinkedHashSet。

 1 package test11;
 2 
 3 import java.util.HashSet;
 4 import java.util.Iterator;
 5 import java.util.Set;
 6 
 7 public class Hash_Demo {
 8     public  static void main(String[] args){
 9         has_Test();
10     }
11     public static void has_Test(){
12         Set<String>  set_str=new HashSet<>();
13         set_str.add("i");
14         set_str.add("j");
15         set_str.add("k");
16         set_str.add("k");
17         set_str.add("m");
18         Iterator<String> set_it=set_str.iterator();
19         while (set_it.hasNext()){
20             System.out.print(set_it.next());
21         }
22     }
23 }

 对于我们常用的javaAPI (String、Ineteger等)的equals和hascode方法已经重写。

HashSet集合存储数据的结构:

首先我们了解什么是哈希表?

哈希表底层使用的也是数组机制,数组中也存放对象,而这些对象往数组中存放时的位置比较特殊,当需要把这些对象给数组中存放的时候,那么根据这些对象的特有的数据结合相应的算法,计算出这个对象在数据中的位置,然后把这个对象存放在数组中而这样的数组叫做哈希数组,也就是哈希表。

        当向哈希表中存放元素时,需要根据元素的特有的数据结合相应的算法,这个算法其实就是Object中的hasCode方法,由于任何对象都是Object的子类,所有任何对象都有hasCode方法,算出对象的在表中的存放位置。这里需要注意,如果两个对象的hasCode方法算出的结果一样,这样叫做哈希冲突,这个时候调用对象的equals方法,比较这两个对象是不是同一个对象,如果equals方法返回true的话,就不会把第二个对象存入哈希表中。如果返回的false的时候,就会把这个值存放在hash表中。

这也就是保证了HashSet集合的元素的唯一性,其实就是根据对象的hascode和equals方法决定的。如果我们往集合存储自定义对象的话,需要我们重写hashcode和equals方法,来适用当前对象比较方法。

 对于javaAPI上面代码。

对于自定义类型,需要重写hascode和equals方法。

 1 package Id_Has;
 2 
 3 
 4 import java.util.HashSet;
 5 import java.util.Iterator;
 6 
 7 public class Id_Set {
 8     public static void main(String[] args){
 9         HashSet<Person_Info>  per_set=new HashSet<>();
10         per_set.add(new Person_Info("tom",22));
11         per_set.add(new Person_Info("tom",22));
12         Iterator<Person_Info> per_it=per_set.iterator();
13         while (per_it.hasNext())
14         {
15             System.out.print(per_it.next());
16         }
17     }
18 
19 }
20 
21 class Person_Info{
22     private String name;
23     private int  age;
24     public  Person_Info(String name,int age){
25         this.name=name;
26         this.age=age;
27     }
28 
29     public String getName() {
30         return name;
31     }
32     public  int getAge(){
33         return  age;
34     }
35     public boolean equals(Object obj){
36         if(obj==null){
37             return  false;
38         }
39         if(obj==this){
40             return true;
41         }
42         if(obj instanceof Person_Info){
43             Person_Info new_obj=(Person_Info)obj;
44             if(new_obj.name.equals(this.name)&& new_obj.age==this.age){
45                 return true;
46             }
47         }
48         return false;
49     }
50     public  int hashCode(){
51         final  int prime=31;
52         int result=1;
53         result=result*prime+age;
54         result=prime * result + ((name == null) ? 0 : name.hashCode());
55         return result;
56    }
57    public String toString(){
58         return "Person_Info[" +this.name+" "+this.age + "]";
59    }
60 }

我们需要对equals方法进行重写以及hashCode方法。

equals方法:

 1     public boolean equals(Object obj){
 2         if(obj==null){
 3             return  false;
 4         }
 5         if(obj==this){
 6             return true;
 7         }
 8         if(obj instanceof Person_Info){
 9             Person_Info new_obj=(Person_Info)obj;
10             if(new_obj.name.equals(this.name)&& new_obj.age==this.age){
11                 return true;
12             }
13         }
14         return false;
15     }

hashCode方法:

1     public  int hashCode(){
2         final  int prime=31;
3         int result=1;
4         result=result*prime+age;
5         result=prime * result + ((name == null) ? 0 : name.hashCode());
6         return result;
7    }

其中hashCode方法的重写,是参考JavaApi进行重写。

字符串的hashCode的重写,其中private int hash; // Default to 0  然后用h的值乘以31在和字符对应的数值进行计算得到一个整数作为字符串的hashCode。因为字符串这种保证了hashCode唯一性。然后我们重写的时候

其中有个值是唯一的就是name的hashCode。

总结如下:

hashCode相等的,不一定对象相等。

对象相等,hashCode相等,

 HashSet的是无序的,存储和遍历,但是如果我们想要存取顺序的怎么办呢?

HashSet的子类有个子类:LinkedHashset。

public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, Serializable

他是链表和Hashset的结合。

code:

 1 package test11;
 2 
 3 import java.util.Iterator;
 4 import java.util.LinkedHashSet;
 5 
 6 public class LinkHash_Demo {
 7     public static void main(String[] args){
 8         linkHashTest();
 9 
10     }
11     public  static  void linkHashTest(){
12         LinkedHashSet<String> link=new LinkedHashSet<>();
13         link.add("e");
14         link.add("f");
15         link.add("g");
16         link.add("i");
17         Iterator<String> linkit=link.iterator();
18         while (linkit.hasNext()){
19             System.out.print(linkit.next());
20         }
21     }
22 }

 输出结果:

二:ArrayList中的判断元素是否重复原理:

非自定义类型,ArrayList在判断一个元素是否包含这个元素,使用的是contains()方法来判断,实际上使用的equals方法来进行判断,非自定义类型的数据类型equals方法都进行重写,自定义类型的数据需要自己重写的方法,

否则判断的是对象的内存地址是否相等。有时候我们判断一个对象是否相等,是通过对象的属性进行判断而不是单单内存地址,内存地址相等一定是同一个对象,但是对象的内存地址不相等并不能说明两个对象不相等。

最终通过equals来遍历数组判断是否有相等元素。

三:Hashset的add方法和contians方法

因为set集合是无序、唯一的。那set如何判断一个元素是否重复呢?

通过hashCode 和equals方法进行判断。首先计算出要添加元素的hashcode  然后跟集合里的元素的hashcode进行判断,如果不相等,则插入元素,如果相等的话,在和集合的元素进行equals方法的比较,返回true

则不添加元素,如果返回false的话,添加元素。

在对自定义类型的进行set操作的时候,需要重写equals和hashCode方法的重写。

 总结:

 1     List与Set集合的区别?
 2 List:
 3                  它是一个有序的集合(元素存与取的顺序相同)
 4                  它可以存储重复的元素            
 5              Set:
 6                  它是一个无序的集合(元素存与取的顺序可能不同)
 7              它不能存储重复的元素
 8     List集合中的特有方法
 9     void add(int index, Object element) 将指定的元素,添加到该集合中的指定位置上
10     Object get(int index)返回集合中指定位置的元素。
11     Object remove(int index) 移除列表中指定位置的元素, 返回的是被移除的元素
12     Object set(int index, Object element)用指定元素替换集合中指定位置的元素,返回值的更新前的元素
13     ArrayList:
14     底层数据结构是数组,查询快,增删慢
15     LinkedList:
16     底层数据结构是链表,查询慢,增删快
17     HashSet:
18     元素唯一,不能重复
19     底层结构是 哈希表结构
20     元素的存与取的顺序不能保证一致
21     如何保证元素的唯一的?
22         重写hashCode() 与 equals()方法
23     LinkedHashSet:
24     元素唯一不能重复
25     底层结构是 哈希表结构 + 链表结构
26     元素的存与取的顺序一致







原文地址:https://www.cnblogs.com/evilliu/p/7839017.html