5.Set接口

一.HashSet类

  1. Set是Collection子接口,模拟了数学上的集的概念

  2. Set只包含了从Collection继承的方法,不过Set无法记住添加的顺序,不允许包含重复的元素.

  3. 当试图添加两个相同元素近Set集合,添加操作失败,add()方法返回false.

  4. Set集合存储特点:

    1. 不允许元素重复
    2. 不会记录元素的先后添加顺序
  5. Set判断两个对象是否相等使用equsls,而不是使用 == ,也就是说,两个对象equals比较返回true.
    Set集合是不会接受这两个对象的.

新建HashSetDemo

public class HashSetDemo {
	public static void main(String[] args) {
		//无序的,不重复的
		Set s = new HashSet();
		s.add("a");
		s.add(3);
		s.add(3);
		s.add(0);
		s.add("Y");		
		System.out.println(s.toString());
		
		//addAll
		Set<String> ss = new HashSet<>();
		ss.add("S");
		ss.add("A");
		ss.add("B");
		ss.add("C");
		System.out.println(ss.toString());		
		Set<String> sss = new HashSet<>();
		sss.add("a");
		sss.add("b");
		sss.add("c");
		ss.addAll(sss);
		System.out.println(ss);
		
		//迭代器
		Iterator it = ss.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
		
		//remove		
		System.out.println(ss.remove("c"));
		System.out.println(ss);
		
		//remove
		System.out.println(ss.removeAll(sss));
		System.out.println(ss);
	}
}

二.equals和hashCode方法

  1. HashSet是Set接口最常用的实现类,顾名思义,底层才用哈希表(散列/hash)算法.
    其底层其实也是一个数组,存在的意义是提供查询速度,插入速度也比较快,但是适用于少量数据的插入
    操作.

  2. 在HashSet中如何判断两个对象是否相同问题:

    1. 两个对象的equals比较相等,返回true,则说明是相同对象
    2. 两个对象的hashCode方法返回值相等
  3. 二者:缺一不可.
    当往HashSet集合中添加新的对象的时候,先会判断该对象和集合对象中hashCode值

    扫描二维码关注公众号,回复: 10437738 查看本文章
    1. 不等:直接把该新的对象存储到hashCode指定的位置
    2. 相等:再继续判断新对象和集合对象中的equals做比较
      1. hashCode相同,equals为true,则视为是同一个对象,则不保存在哈希表中
      2. hashCode相同,equals为false:非常麻烦,存储在之前对象同槽位的链表上(拒绝)
  4. 不同数据类型的hashCode值
    Eclipse可以自动生成hashCode和equals方法

image

  1. 对象的hashCode和equals方法的重要性:
  2. 每一个存储到hash表中的对象,都得提供hashCode和equals方法,用来判断是否同一个对象.
  3. 存储在哈希表中的对象,都应该覆盖equals方法和hashCode方法.并且保证equals相等的时候,hashCode
    也应该相等.
  4. 如果需要把我们自定义的对象存储到哈希表中,该类型,的对象应该覆盖equal和hashCode方法,
    并在该方法中提供自己的判断规则.
  5. 可以使用Eclipse工具自动生成hashCode和equals方法.

新建类HashSetDemo2

//证明只有覆盖了equals和hashCode对象才能相等
class A{
	public boolean equals(Object obj) {
		return true;
	}
}
class B{
	public int hashCode() {
		return 1;
	}
}
class C{
	public boolean equals(Object obj) {
		return true;
	}
	public int hashCode() {
		return 2;
	}
}
public class HashSetDemo2 {
	public static void main(String[] args) {
		Set set = new HashSet();
		set.add(new A());
		set.add(new A());
		set.add(new B());
		set.add(new B());
		set.add(new C());
		set.add(new C());
		Iterator it = set.iterator();
		while (it.hasNext()) {			
			System.out.println(it.next());
		}
		
	}
}

新建类HashSetDemo3

//hashCode equals
class Student {
	//定义name , age ,score
	private String name;
	private Integer age;
	private Double score;

	//构造方法

	public Student(String name, Integer age, Double score) {
		super();
		this.name = name;
		this.age = age;
		this.score = score;
	}

	//hashCode 和equals
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((age == null) ? 0 : age.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((score == null) ? 0 : score.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age == null) {
			if (other.age != null)
				return false;
		} else if (!age.equals(other.age))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (score == null) {
			if (other.score != null)
				return false;
		} else if (!score.equals(other.score))
			return false;
		return true;
	}

}

public class HashSetDemo3 {
	public static void main(String[] args) {
		//测试
		Set set = new HashSet();
		set.add(new Student("张三", 15, 78.8));
		set.add(new Student("李四", 15, 78.8));
		set.add(new Student("张思", 15, 89.8));
		set.add(new Student("张三", 15, 78.8));
		set.add(new Student("张三", 34, 78.8));
		//name age score 相同的才为同一个对象
		System.out.println(set.size());
		//使用foreach遍历
		for (Object ele : set) {
			System.out.println(ele);
		}
	}
}

三.LinkedHashSet类

  1. List接口:允许元素重复,记录先后添加顺序
  2. Set接口:不允许元素重复,不记录先后添加顺序
  3. 需求,不允许元素重复,但是需要保证先后添加顺序
  4. LinkedHashSet:底层才有哈希表和链表算法.
  5. 哈希表:来保证唯一性,此时就是HashSet,在哈希表中元素没有先后顺序.
  6. 链表:来记录元素的先后添加顺序.

image

新建类LinkedHashSetDemo

//LinkedHashSet
public class LinkedHashSetDemo {
	public static void main(String[] args) {
		//有序
		Set set = new LinkedHashSet();
		set.add("sdsa");
		set.add("sd");
		set.add(89);
		set.add(1);
		System.out.println(set);
	}
}

四.TreeSet类

注意:必须保证TreeSet集合中的元素对象是相同的数据类型,否则报错.

新建类TreeSetDemo

public class TreeSetDemo {

	public static void main(String[] args) {
		TreeSet<String> tset = new TreeSet<>();
		tset.add("B");
		tset.add("A");
		tset.add("10");
		tset.add("08");
		System.out.println(tset);
		System.out.println(tset.first());
		System.out.println(tset.last());
		System.out.println(tset.headSet("10"));
	}

}

image

五.自然排序-Comparable

  1. 自然排序(从小到大):
    TreeSet调用集合元素的comparableTo方法来比较元素的大小关系,然后讲集合元素按照升序排列
    java.util.Comparable接口,可比较的
    覆盖public int compareTo方法,在该方法,里写比较规则.
  2. 在该方法中,比较当前对象(this)和参数对象o做比较(严格来说是对象中的数据,比如按照对象的年龄)
this > o;返回正整数;
this < o;返回负整数
this == o;返回0
  1. 在TreeSet的自然排序中,认为如果两个对象,做比较的compareTo方法返回的是0,则认为是同一个对象.

新建类TrreSetDemo

class Person implements Comparable<Person> {
	private String name;
	private int age;

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

	@Override
	public int compareTo(Person other) {
		if (this.age > other.age) {
			return 1;
		} else if (this.age < other.age) {
			return -1;
		} else
			return 0;
	}

}

public class TreeSetDemo2 {

	public static void main(String[] args) {
		TreeSet<Person> ts = new TreeSet<>();
		ts.add(new Person("庄塞", 14));
		ts.add(new Person("接口塞", 74));
		ts.add(new Person("庄送到", 34));
		ts.add(new Person("里塞", 24));

		Iterator it = ts.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
	}

}

image

image

六.定制排序

(从大到小,按照名字的长短来排序等)

  1. 在TreeSet构造器中传递java.lang.Comparator对象,并覆盖public int compare(Object o1,Object o2)
    在编写比较规则

![image][sort]

  1. TreeSet集合来说,要么使用自然排序,要么使用定制排序
自然排序(comparable):CompareTo方法返回0
定制排序(comparator):Compare方法返回0

修改类TreeSetDemo

//自然排序
class Person implements Comparable<Person> {
	...
}

//名字长度比较器
class NameLenthComparator implements Comparator<Person> {
	//编写比较规则
	@Override
	public int compare(Person o1, Person o2) {
		if (o1.name.length() > o2.name.length()) {
			return 1;
		} else if (o1.name.length() < o2.name.length()) {
			return -1;
		}else {
			//加入名字的长度相同,在按年龄大小排序
			if (o1.age > o2.age) {
				return 1;
			} else if (o1.age < o2.age) {
				return -1;
			} else
				return 0;
		}
	}
}

public class TreeSetDemo2 {

	public static void main(String[] args) {
....

		System.out.println("---------implements Comparator-------------");
		TreeSet<Person> ts1 = new TreeSet<>(new NameLenthComparator());
		ts1.add(new Person("庄塞", 14));
		ts1.add(new Person("接口塞", 74));
		ts1.add(new Person("庄送到", 34));
		ts1.add(new Person("西门吹牛", 24));
		//使用迭代器迭代
		Iterator it1 = ts1.iterator();
		while (it1.hasNext()) {
			System.out.println(it1.next());
		}
	}

}

七.Set实现类的特点和性能

  1. 共同特点
    1. 都不允许元素重复
    2. 都不是线程安全的类
解决方案:Set s = Collections.synchronizedSet(Set对象);
  1. HashSet:底层采用哈希表算法,查询效率极高.
    判断两个对象是否相等的规则:

    1. equals比较为true
    2. hashCode值相等
  2. 要求存在哈希表中的对象元素都得覆盖equals和hashCode方法

  3. LinkedHashSet:
    HashSet的子类,底层也采用的是哈希表算法,但是也使用了链表算法来维持元素的先后添加顺序,
    判断两个对象相同的规则和HashSet相同
    因为需要多使用一个链表来记录元素的顺序,所以性能相对HashSet较低
    一般少用,如果要求一个集合,既要保证元素不重复,也需要记录添加先后顺序,才选择使用
    linkedHashSet

  4. TreeSet:不保证元素的先后添加顺序,但是会对集合中的元素做排序操作,
    底层才有红黑树算法(树结构,比较擅长做范围查询)
    TrreSet要么采用自然排序,要么定制排序

  5. 自然排序:要求在TreeSet集合中的对象必须实现java.lang.Comparable接口,并覆盖compareTo方法

  6. 定制排序:要求在构建TreeSet对象的时候,传入一个比较器对象(必须实现java.lang.Comparator接口)
    在比较器中覆盖compare方法,并编写比较规则

  7. TreeSet判断元素对象重复规则:
    compare/compareTo方法是否返回0,如果返回0,则视为是同一个对象.

  8. HashSet做等值查询效率高,TreeSet做范围查询效率高.
    而我们更多的情况,都是做等值查询,在数据库的索引中做范围查询较多,所以数据结构主要用于做,
    索引,用来提高查询效率

发布了66 篇原创文章 · 获赞 0 · 访问量 1043

猜你喜欢

转载自blog.csdn.net/huang_kuh/article/details/105289490