一、Set
(1)Set简介
Set接口是单列集合(Collection)的另一个重要的分支,Set集合中元素无序且不可重复,不能通过下标访问。HashSet 和 TreeSet 是 Set 接口的两个实现类。
(2)常用方法
方法 | 描述 |
add(E e) | 确保此 collection 包含指定的元素(可选操作)。 |
addAll(Collection<? extends E> c) | 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
clear() | 移除此 collection 中的所有元素(可选操作)。 |
contains(Object o) | 如果此 collection 包含指定的元素,则返回true。 |
containsAll(Collection<?> c) | 如果此 collection 包含指定 collection 中的所有元素,则返回 true。 |
equals(Object o) | 比较此 collection 与指定对象是否相等。 |
isEmpty() | 如果此 collection 不包含元素,则返回true。 |
iterator() | 返回在此 collection 的元素上进行迭代的迭代器。 |
remove(Object o) | 从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。 |
removeAll(Collection<?> c) | 移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。 |
retainAll(Collection<?> c) | 仅保留此Collection中那些也包含在指定 Collection 的元素 |
size() | 返回此Collection中元素的位数 |
toArray() | 返回包含此Collection中所有元素的数组 |
因为 List 和 Set 都实现了Collection 接口,所以两类集合中的方法基本都是从父类中继承过来的,所以 用法基本相同。
二、HashSet
(1)简介
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{}
HashSet继承了AbstractSet类,并实现了Set接口、Cloneable接口 和 Serializable接口。
实现原理:hash表(数组+链表)。
HashSet不允许集合中出现重复元素。当我们提到HashSet时,第一件事就是在将对象存储在HashSet之前,要确保重写hashCode()方法和equals()方法,这样才能比较对象的值是否相等,确保集合中没有储存相同的对象。如果不重写上述两个方法,那么将使用下面方法默认实现:
public boolean add(Object obj)方法用在Set添加元素时,如果元素值重复时返回 "false",如果添加成功则返回"true"。
(2)HashSet的构造方法(5个):
/**
* 默认的无参构造器,构造一个空的HashSet。
*
* 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
*/
public HashSet() {
map = new HashMap<E,Object>();
}
/**
* 构造一个包含指定collection中的元素的新set。
*
* 实际底层使用默认的加载因子0.75和足以包含指定collection中所有元素的初始容量来创建一个HashMap。
* @param c 其中的元素将存放在此set中的collection。
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* 以指定的initialCapacity和loadFactor构造一个空的HashSet。
*
* 实际底层以相应的参数构造一个空的HashMap。
* @param initialCapacity 初始容量。
* @param loadFactor 加载因子。
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
/**
* 以指定的initialCapacity构造一个空的HashSet。
*
* 实际底层以相应的参数及加载因子loadFactor为0.75构造一个空的HashMap。
* @param initialCapacity 初始容量。
*/
public HashSet(int initialCapacity) {
map = new HashMap<E,Object>(initialCapacity);
}
/**
* 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。此构造函数为包访问权限,不对外公开,
* 实际只是是对LinkedHashSet的支持。
*
* 实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。
* @param initialCapacity 初始容量。
* @param loadFactor 加载因子。
* @param dummy 标记。
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}
(3)HashSet的使用
新建一个 Student 类,并重写 hashcode() 和 equals() 方法。
package basis.CollectionStu;
import java.util.Objects;
public class Student {
private String name;
private int age;
private String phone;
public Student(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
public Student() {
}
//省略getter和setter
//重写hashcode()和equals()方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return getAge() == student.getAge() &&
Objects.equals(getName(), student.getName()) &&
Objects.equals(getPhone(), student.getPhone());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge(), getPhone());
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
}
主类:
package basis.CollectionStu;
import java.util.HashSet;
import java.util.Iterator;
public class StuHashSet {
public static void main(String[] args) {
HashSet<Student> students = new HashSet();
//添加
Student student_1 = new Student("suxing",21,"11111");
Student student_2 = new Student("lisi",22,"222222");
Student student_3 = new Student("zhangsan",23,"333333");
students.add(student_1);
students.add(student_2);
students.add(student_3);
students.add(student_1);
//遍历
Iterator<Student> it = students.iterator();
while (it.hasNext()){
System.out.println(it.next().toString());
}
}
}
输出结果:
Student{name='zhangsan', age=23, phone='333333'}
Student{name='suxing', age=21, phone='11111'}
Student{name='lisi', age=22, phone='222222'}
结果表明:输出顺序跟我们插入的顺序并不一致,可见,重写了hashcode()方法和equals()方法,实现了元素重排。并且相同的对象不能再次插入到集合中。
三、TreeSet
(1)简介
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{}
- TreeSet继承了AbstractSet抽象类,所以它是一个set集合,可以被实例化,且具有set的属性和方法,并实现了Cloneable接口、NavigableSet接口 和 Serializable接口。
- TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
- TreeSet的性能比HashSet差但是我们在需要排序的时候可以用TreeSet因为他是自然排序也就是升序下面是TreeSet实现代码这个类也似只能通过迭代器迭代元素
(2)构造方法
//默认无参构造
public TreeSet() {
this(new TreeMap<E,Object>());
}
//传入一个指定的比较器
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
//传入一个指定的Collection,使新的TreeSet包含此Collection中的所有元素
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
//传入一个SortedSet
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
SortedSet是Set的一个子接口:
public interface SortedSet<E> extends Set<E> {}
(3)TreeSet的使用
TreeSet有两种排序方法:自然排序和定制排序。默认采用自然排序。
要求自定义类实现java.lang.Comparable接口并重写其compareTo(Object obj)的抽象方法在此方法中,指明按照自定义类的哪个属性进行排序。向TreeSet中添加元素时,首先按照compareTo()进行比较,一旦返回0,虽然仅是两个对象的此属性值相同,但是程序会认为这两个对象是相同的,进而后一个对象就不能添加进来。因此要求compareTo()与hashCode()以及equals()三者保持一致!
定义一个Person 类,实现compareable 接口,重写 compareTo() 方法。
package basis.CollectionStu;
public class Person implements Comparable {
private String name;
private int age;
private String phone;
public Person() {}
public Person(String name, int age, String phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
//省略getter和setter
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
@Override
public int compareTo(Object o) {
Person person = (Person) o;
//根据年龄进行比较
return this.age>person.age?1:(this.age==person.age?0:-1);
}
}
主类:
package basis.CollectionStu;
import java.util.Iterator;
import java.util.TreeSet;
public class StuTreeSet {
public static void main(String[] args) {
TreeSet<Person> persons = new TreeSet();
//添加
Person person_1 = new Person("suxing",23,"11111");
Person person_2 = new Person("lisi",21,"22222");
Person person_3 = new Person("wangwu",20,"33333");
Person person_4 = new Person("zhaoliu",23,"44444");
persons.add(person_1);
persons.add(person_2);
persons.add(person_3);
persons.add(person_4);
//遍历
Iterator<Person> it = persons.iterator();
while (it.hasNext()){
System.out.println(it.next().toString());
}
}
}
运行结果:
Person{name='wangwu', age=20, phone='33333'}
Person{name='lisi', age=21, phone='22222'}
Person{name='suxing', age=23, phone='11111'}
由结果可以看出,实现compareable 接口,重写 compareTo() 方法,实现了集合中的元素按照指定的方法进行排序,而且年龄重复的元素不再插入到集合中。