概述
- 集合又称对象的容器,定义了对多个对象进行操作的常用方法。可实现数组的功能。
- 与数组的区别:
- (1)数组长度固定,集合长度可变
- (2)数组可以存储基本类型和引用类型,集合只能存储引用类型
- 位置:
java.util.*
各集合继承关系如图:
JDK快速访问地址
Set 接口
java.util.set
接口继承 Collection
接口
特点:不能存储重复元素;存取无序;没有索引(即不能使用普通的 for 循环遍历)
(一)HashSet
java.util.HashSet
集合实现 Set
接口
特点:底层是哈希表结构(查询速度快),基于HashCode 实现元素不重复
存储过程:
- ① 根据hashcode计算保存的位置,如果此位置为空,则直接保存,若不为空则执行 ② 。
- ② 执行equals 方法,如果equals 方法为true,则认为是重复的,否则,形成链表。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author Nigori
* @date 2020/5/13
**/
public class SetTest_01 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("aaa");
set.add("ccc");
set.add("ccc");
set.add("ggg");
//使用迭代器或者foreach遍历set集合
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
结果展示:
为什么不能存储重复的元素?
答:当存入元素的哈希码相同时,会调用equals()
进行确认,如果为true
,则拒绝后者元素存入。
哈希值为一个逻辑地址。在Object 类中 int hashCode() 返回该对象的哈希码值。
public native int hashCode(); //native:代表该方法调用本地操作系统的方法
哈希表结构图解:
代码解释:
HashSet 存储自定义类型(重点)
重写 hashCode() and equals() 方法 ★★★
测试类 HashSetTest.java
import java.util.HashSet;
import java.util.Set;
/**
* @author Nigori
* @date 2020/5/13
**/
public class HashSetTest {
public static void main(String[] args) {
addHashTest();
}
public static void addHashTest() {
Set<Person> setPerson = new HashSet<>();
Person person_1 = new Person("小明",20);
Person person_2 = new Person("小明",20);
Person person_3 = new Person("小明",21);
System.out.println(person_1.hashCode()); //1908153060 //23458774
System.out.println(person_2.hashCode()); //116211441 //3458774
System.out.println(person_3.hashCode()); //607635164 //23458775
System.out.println(person_1 == person_2); //false //false
System.out.println(person_1.equals(person_2)); //false //true
setPerson.add(person_1);
setPerson.add(person_2);
setPerson.add(person_3);
System.out.println(setPerson);
//[Person{name='小明', age=20}, Person{name='小明', age=21}, Person{name='小明', age=20}]
//[Person{name='小明', age=21}, Person{name='小明', age=20}] 重写 hashCode() and equals()
//setPerson.remove(person_1);
setPerson.remove(new Person("小明",21)); //必须重写hashCode() 和 equals()
System.out.println(setPerson); //[Person{name='小明', age=20}]
}
}
自定义类型 Person.java
import java.util.Objects;
/**
* @author Nigori
* @date 2020/5/13
**/
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, 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;
}
}
(二)LinkedHashSet
java.util.LinkedHashSet
继承自 HashSet
类
特点:底层是一个哈希表(数组+链表/红黑树) + 链表的结构,后者链表用来记录元素的存储顺序,保证元素有序 。但不允许有重复元素。
(三)TreeSet
特点:红黑树的存储结构,基于排列顺序实现元素不重复;实现了SortedSet
接口,对集合元素自动排序。
这里主要介绍:自定义类型
//Person 没有实现 Comparable 接口,报 ClassCastException 异常
TreeSet<Person> treeSet = new TreeSet<>();
Person person_1 = new Person("zds",20);
Person person_2 = new Person("thf",22);
Person person_3 = new Person("asg",21);
treeSet.add(person_1);
treeSet.add(person_2);
treeSet.add(person_3);
System.out.println(treeSet.size());
方式一:通过实现 Comparable< T > 接口,重写 compareTo() 方法,返回值为 0,认为重复
在Person
类中有:
public class Person implements Comparable<Person> {
private String name;
private int age;
/* 省略了构造方法和getter,setter方法 */
@Override
public int compareTo(Person o) {
int numb1 = this.getName().compareTo(o.getName());
int numb2 = this.getAge() - o.getAge();
return numb1 == 0 ? numb2 : numb1;
}
}
TreeSet<Person> treeSet = new TreeSet<>();
Person person_1 = new Person("zds",20);
Person person_2 = new Person("thf",22);
Person person_3 = new Person("asg",21);
treeSet.add(person_1);
treeSet.add(person_2);
treeSet.add(person_3);
System.out.println(treeSet.size()); // 3
System.out.println(treeSet); //[Person{name='asg', age=21}, Person{name='thf', age=22}, Person{name='zds', age=20}]
方式二:通过实现 Comparator< T > 接口,重写 compare(T o1, T o2); 方法
TreeSet<Person> treeSet = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
int num1 = o1.getAge() - o2.getAge();
int num2 = o1.getName().compareTo(o2.getName());
return num1 == 0 ? num2 : num1;
}
});
Person person_1 = new Person("zds",20);
Person person_2 = new Person("thf",22);
Person person_3 = new Person("asg",21);
treeSet.add(person_1);
treeSet.add(person_2);
treeSet.add(person_3);
System.out.println(treeSet.size()); // 3
System.out.println(treeSet); //[Person{name='zds', age=20}, Person{name='asg', age=21}, Person{name='thf', age=22}]