一:
和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 元素的存与取的顺序一致