Set接口:存储无序的,不可重复的数据
HashSet:Set的主要实现类;线程不安全;可以存储null值
LinkedHashSet:HashSet的子类;遍历其内部数据时,可以按添加的顺序遍历
TreeSet:可以按照添加的元素的指定属性进行排序。
Set接口中没有额外新定义方法,使用的都是Collection中的方法。
要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
重写的hashCode()和equals()方法尽可能保持一致:相等的对象必须具有相等的散列码
* 重写两个方法的技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode值
Set:存储无需的、不可重复的数据<br /> 1、无序性:<br /> * 无序性不等于随机性。存储的数据在数组中并非按照数组索引顺序添加,而是根据数据的哈希值决定的<br /> 2、不可重复性:<br /> * 添加的元素按照`equals()`判断时,不能返回`true`,即相同的元素只能添加一个。<br /> *添加元素的过程:以HashSet为例:<br /> 向`HashSet`中添加元素a,首先调用元素a所在类的`HashCode()`方法,计算元素a的哈希值,<br /> 此哈希值通过某种算法计算出在`HashSet`底层数组中的存放位置(即为索引位置),判断数组<br /> 此位置上是否为`null`:<br /> 1) 如果为`null`,则元素a添加成功。<br /> 如果此位置上有其他元素b,比较两者哈希值<br /> 2)如果哈希值不同,则添加成功(链表的形式)<br /> 3)如果哈希值相同,则调用元素a所在类的`equals()`方法:<br /> `equals()`方法返回`ture`,则添加失败,反之添加成功。对于添加成功的情况2和情况3而言:元素a与已经存在指定所应位置上的数据以链表的方式进行添加。<br /> 存储。<br /> 在JDK7中,元素a放到数组中,指向原来的元素;<br /> 在JDK8中,原来的元素在数组中,指向元素a。<br /> */
HashSet测试代码:
package com.SetTest;import com.CollectionTest.Person;import org.junit.Test;import java.util.*;public class SetTest {@Testpublic void test1(){Set set = new LinkedHashSet();set.add(456);set.add(123);set.add("AA");set.add("CC");set.add(new Person("Tom",12));set.add(new User("Tom",12));set.add(new User("Tom",12));set.add(129);Iterator iterator = set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}}class User{private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return age == user.age && Objects.equals(name, user.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}}
注:如果不重写hashCode()方法,那么equals()方法会直接去调两个对象的地址值,显然两个分别实例化的对象的地址值是不同的,所以就会添加成功。
LinkedHashSet():
LinkedHashSet底层的存储结构:
双向链表和随机储存结合。
这样设计是为了应对频繁的遍历操作
HashSet重要例题:
public void test2(){HashSet set = new HashSet();Person p1 = new Person(1001,"AA");Person p2 = new Person(1002,"BB");set.add(p1);set.add(p2);p1.name = "CC";set.remove(p1);System.out.println(set);//这里Person类重写了equals()和hashCode()方法之后//对象实际得索引位置变了,但是存在set里的索引还没变,所以删除失败set.add(new Person(1001,"CC"));//HashSet中p1对象的实际位置是空的,所以//能加进去System.out.println(set);set.add(new Person(1001,"AA"));//这里把这个对象给链进去了System.out.println(set);}
