Set接口的特性:Set接口继承了Collection接口,Set集合中不能包含重复的元素,每个元素必须是唯一的,且集合中的元素是无序的。

Set接口的三个实现类:

一、HashSet类:
HashSet的实现原理其实是HashMap,只不过它的key也是value。采用hash表算法来实现,存放的元素是不重复,且无序的。

如何判断两个元素重复?

通过hashCode和equals方法来保证元素的唯一性,add方法返回的是boolean类型。

首先,通过判断元素的hashCode值是否一致,只有在该值相同的情况下,才会进行equals判断。如果存储在HashSet中的两个对象HashCode值和equals方法返回的结果都相同时,那么认为这两个元素是相同元素,只能存储一个。

注意:hashCode值相同,而equals不相同的两个元素是不同的,只不过通过存储在同一个hashCode位置上而已。如图2:

java set实现不重复 java set为什么不能重复_java


应用场景:要一个能快速访问的Set,就使用HashSet。
如果我们添加自定义类的对象到HashSet中时,要重写HashCode方法和equals方法,来确保有相同属性的两个对象具有相同的hashCode值,判断它重复。

二、TreeSet类:
TreeSet是采用树结构实现(称为红黑树算法),元素是有序且不重复的,主要有add,remove,contains方法,时间复杂度都为O(log(n))。还提供一些处理排序的set方法,如first(),headSet(),tailSet().

因为是有序的,所以输入和输出的顺序是不同的,输出是按顺序来输出的。

TreeSet集合排序的两种方式:

  1. 让元素自身具备比较性:即传入的元素自身要实现Comparable接口,覆盖compareTo方法。
    这种方式也作为元素的自然排序,称为默认排序。
public class Demo4 {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet();
		ts.add(new Person("aa", 20, "男"));
		ts.add(new Person("bb", 18, "女"));
		ts.add(new Person("cc", 17, "男"));
		ts.add(new Person("dd", 17, "女"));
		ts.add(new Person("dd", 15, "女"));
		ts.add(new Person("dd", 15, "女"));
 
 
		System.out.println(ts);
		System.out.println(ts.size()); // 5
 
	}
}
 
class Person implements Comparable {
	private String name;
	private int age;
	private String gender;
 
	public Person() {
 
	}
 
	public Person(String name, int age, String gender) {
 
		 = name;
		this.age = age;
		this.gender = gender;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		 = name;
	}
 
	public int getAge() {
		return age;
	}
 
	public void setAge(int age) {
		this.age = age;
	}
 
	public String getGender() {
		return gender;
	}
 
	public void setGender(String gender) {
		this.gender = gender;
	}
 
	@Override
	public int hashCode() {
		return name.hashCode() + age * 37;
	}
 
	public boolean equals(Object obj) {
		System.err.println(this + "equals :" + obj);
		if (!(obj instanceof Person)) {
			return false;
		}
		Person p = (Person) obj;
		return .equals() && this.age == p.age;
 
	}
 
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", gender=" + gender
				+ "]";
	}
 
	@Override
	public int compareTo(Object obj) {
		
		Person p = (Person) obj;
		System.out.println(this+" compareTo:"+p);
		if (this.age > p.age) {
			return 1;
		}
		if (this.age < p.age) {
			return -1;
		}
		return .compareTo(); 	//当compareTo()函数返回值为0时,说明两个对象相等
	}
 
}
  1. 让容器自身具备比较性,自定义比较器:即当元素自身不具备比较性,或具备的比较性不是所需的时,
    那么这时候只能让容器自身具备比较性。
    定义一个类实现Comparator接口,覆盖compare方法,并将该类的对象作为参数传递给TreeSet集合的构造函数。
    注意:当元素自身的比较器Comparable和容器的比较器Comparator同时存在的时候,以容器的Comparator为主。
public class Demo5 {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet(new MyComparator()); 		//该类的对象作为参数传入到TreeSet中
		ts.add(new Book("think in java", 100));
		ts.add(new Book("java 核心技术", 75));
		ts.add(new Book("现代操作系统", 50));
		ts.add(new Book("java就业教程", 35));
		ts.add(new Book("think in java", 100));
		ts.add(new Book("ccc in java", 100));
 
		System.out.println(ts); 
	}
}
 //定义一个类实现Comparator接口,覆盖compare方法
class MyComparator implements Comparator {
 
	public int compare(Object o1, Object o2) {
		Book b1 = (Book) o1;
		Book b2 = (Book) o2;
		System.out.println(b1+" comparator "+b2);
		if (b1.getPrice() > b2.getPrice()) {
			return 1;
		}
		if (b1.getPrice() < b2.getPrice()) {
			return -1;
		}
		return b1.getName().compareTo(b2.getName());
	}
 
}
 
class Book {
	private String name;
	private double price;
 
	public Book() {
 
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		 = name;
	}
 
	public double getPrice() {
		return price;
	}
 
	public void setPrice(double price) {
		this.price = price;
	}
 
	public Book(String name, double price) {
 
		 = name;
		this.price = price;
	}
 
	@Override
	public String toString() {
		return "Book [name=" + name + ", price=" + price + "]";
	}
 
}

注意:在重写compareTo或compare方法时,必须要明确比较的主要条件相等时,要比较次要条件。
即姓名相同时,要比较年龄是否相同。

三、LinekHashSet类:
介于HashSet和TreeSet之间,也是一个hash表,但它同时维护了一个双链表来记录插入的顺序,基本方法的复杂度为O(1)。

LinkedHashSet dset = new LinkedHashSet();
 
dset.add(new Apple(7));
 
dset.add(new Apple(6));
 
dset.add(new Apple(8));
 
dset.add(new Apple(10));
 
dset.add(new Apple(9));
 
Iterator iterator = dset.iterator();
 
while (iterator.hasNext()) {
 
System.out.print(iterator.next() + " ");
 
}

输入和输出的顺序是一样的,因为它有记录插入时的顺序。

记忆:

  • 看到array,就要想到角标。
  • 看到link,就要想到first,last。
  • 看到hash,就要想到hashCode,equals.
  • 看到tree,就要想到两个接口。Comparable,Comparator。