今天学习的内容是集合类之HashSet和LinkedHashSet
与List接口不同,Set接口与Collection接口的方法完全一致。常用的集有HashSet、LinkedHashSet和TreeSet,今天先学习HashSet和LinkedHashSet。
HashSet的底层数据结构是哈希表,是不同步的。HashSet在存储元素时是以该元素的哈希值确定存储位置,在随机访问集中的元素时,不需要从头开始查找,根据哈希值就可以快速找到元素,所以HashSet提供了最快的查询速度。注意HashSet可以存储null。
那么HashSet是如何判断元素是否重复呢?在存储元素时,HashSet会先调用该元素的HashCode()方法得到该元素的哈希值,与集中元素的哈希值比较,判断该元素是否与集中的元素重复,如果不重复就直接存储该元素。但是由于算法的原因,不同对象的哈希值有可能相同,所以如果该元素的哈希值与集中某元素相同,HashSet还要使用equals()方法比较这两个元素是否相等。如果返回true,不会存储该元素;如果返回false,此时可以选择顺延或者串联存储。所以,当使用集存储自定义类的对象时,必须要覆盖HashCode()方法与equals()方法。
由于HashSet是根据哈希值确定元素的存储位置,所以HashSet是无序的。针对这个问题,JavaAPI提供了一个HashSet的子类:LinkedHashSet,它的底层数据结构是链表和哈希表,所以LinkedHashSet是有序的,元素的顺序就是添加的顺序。
HashSet和LinkedHashSet的示例程序如下:
public class Test55 {
public static void main(String[] args) {
// HashSet的方法都是Collection的方法
HashSet<String> set = new HashSet<>();
set.add("a");
set.add("a");
set.add("b");
set.add("c");
System.out.println(set);// [b, c, a] 元素不允许重复,且元素是无序的
Iterator<String> it = set.iterator();
while (it.hasNext()) {// 由于Set接口没有定义get()方法,所以只能用这种方式获取元素
System.out.print(it.next());// bca
}
// LinkedHashSet是有序的
LinkedHashSet<String> l_set = new LinkedHashSet<>();
l_set.add("a");
l_set.add("a");
l_set.add("b");
l_set.add("c");
System.out.println(l_set);// [a, b, c] 元素不允许重复,且元素是有序的
// 使用HashSet存储自定义对象
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三", 21));
hs.add(new Person("张三", 21));
hs.add(new Person("李四", 21));
hs.add(new Person("王五", 21));
System.out.println(hs);// [Person [name=王五, age=21], Person [name=张三,
// age=21], Person [name=李四, age=21]]
Iterator<Person> it_p = hs.iterator();
while (it_p.hasNext()) {
Person p = it_p.next();
System.out.println(p);// 具有相同name和age的Person对象会被视作同一个对象,不会重复存储
}
}
}
// 要覆盖Person的hashCode()与equals()方法
class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() { // 覆盖Object的hashCode()方法
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {// 覆盖Object的equals()方法
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
补充一点,列表判断元素是否相等(比如调用contains()方法时)只判断equals()方法,如下示例程序:
public class Test56 {
public static void main(String[] args) {
// 定义一个临时列表存储不重复的元素
ArrayList<Person> al = new ArrayList<>();
al.add(new Person("张三", 21));
al.add(new Person("张三", 21));
al.add(new Person("李四", 21));
al.add(new Person("王五", 21));
System.out.println(al);// 会存储相同name与age的Person对象
al = getSingleElement(al);//调用了四次equals()方法
System.out.println(al);// 不存储相同name与age的Person对象
}
public static ArrayList<Person> getSingleElement(ArrayList<Person> al) {
// 定义一个临时列表
ArrayList<Person> temp = new ArrayList<>();
// 迭代al
Iterator<Person> it = al.iterator();
while (it.hasNext()) {
Person p = it.next();
// 判断该元素是否存在于临时列表中,如不存在,添加进临时列表
if (!temp.contains(p)) {// 列表判断元素相同的依据:只判断equals()方法
temp.add(p);
}
}
return temp;
}
}
class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() { // 覆盖Object的hashCode()方法
System.out.println("hashCode()");
return age;
}
@Override
public boolean equals(Object obj) {// 覆盖Object的equals()方法
System.out.println("equals()");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}