Java 基础学习之类集框架四 (Set、SortedSet)

1. 基本概念

  1. Set 接口也属于 Collection 的常用接口,其最大特点是不允许出现重复数据。
  2. Set 接口的定义
    (1) Set 接口也是 Collection 的子接口,定义:public interface Set<E> extends Collection<E>
    (2)Set 接口主要方法与 Collection 是一致的,也就是说 Set 接口并没有对 Collection 接口进行扩充,只是比 Collection 接口的要求更加严格,不能增加重复的元素。
    (3)Set 接口的实例无法像 List 接口那样可以进行双向输出,因为此接口没有提供像 List 接口定义的 get(int index ) 方法。

2. Set 接口的常用子类

   Set 接口中有HashSet(散列存放) 和 TreeSet( 有序存放)两个常用子类,这两个子类在使用时都会有自己的实现要求。

2.1 散列存放:HashSet

   HashSet 是 Set 的一个子类,主要特点是:里面不能存放重复的元素,而且是采用散列的存储方式,所以没有顺序。

实例1代码:
package self.learn.setdemo;

import java.util.HashSet;
import java.util.Set;

public class SetDemo {

	public static void main(String[] args) {
		Set<String> allSet = new HashSet<String>();
		allSet.add("A");               // 增加元素
		allSet.add("B");
		allSet.add("C");
		allSet.add("C");               // 重复元素,不能加人
		allSet.add("C");               // 重复元素,不能加人
		allSet.add("D");
		System.out.println(allSet);    // 输出集合对象,调用 toString()方法
	}
}
运行结果截图:

在这里插入图片描述
   从结果中可以看出,对于重复的元素只会增加一次,而且程序运行时向集合中加入元素的顺序并不是集合中的保存顺序,证明 HashSet 类中的元素是无序存放的。

2.2 有序的存放:TreeSet

   如果想对输入的数据进行有序排列,则要使用 TreeSet 子类。TreeSet 类定义如下:

public class TreeSet<E> extends AbstractSet<E>
implements Sorted<E>,Cloneable,Serializable

TreeSet 类 也是继承了 AbstractSet 类,此类定义如下:

public abstract class AbstractSet<E> extends AbstractCollection<E>
implements Set<E>
实例2代码:
package self.learn.setdemo;

import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
	public static void main(String[] args) {
		Set<String> allSet = new TreeSet<String>();
		allSet.add("D");
		allSet.add("C");
		allSet.add("C");               // 重复元素,不能加人
		allSet.add("C");               // 重复元素,不能加人		
		allSet.add("A");               // 增加元素
		allSet.add("B");
		System.out.println(allSet);    // 输出集合对象,调用 toString()方法
	}
}
运行结果截图:

在这里插入图片描述

2.3 关于 TreeSet 的排序说明:自定义类排序

自定义 Person 类
package self.learn.setdemo;

public class 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 + "]";
	}	
}
package self.learn.setdemo;

import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
	public static void main(String[] args) {
		Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
		allSet.add(new Person("张三", 30));
		allSet.add(new Person("李四", 31));
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人		
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人
		allSet.add(new Person("赵六", 33));
		allSet.add(new Person("孙七", 33));               // 年龄重复
		System.out.println(allSet);    // 输出集合对象,调用 toString()方法
	}
}
运行结果截图:

在这里插入图片描述
   由图可知,出现了类转换异常,存现这样的问题是因为 TreeSet 中的元素是有序存放的,所以对于一个对象必须指定好其排序规则,且 TreeSet 中的每个对象所在类都必须实现 Comparable 接口才可以正常使用。

person 类指定好排序规则后

package self.learn.setdemo;

public class Person implements Comparable<Person>{             // 定义 Person 类实现比较器
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {                               // 覆写 toString() 方法
		return "Person [name=" + name + ", age=" + age + "]";
	}	
	public int compareTo(Person per) {         // 覆写 compareTo() 方法,指定排序规则
		if(this.age > per.age) {               // 通过年龄排序
			return 1;
		}else if (this.age < per.age) {
			return -1;
		}else {
			return 0;
		}
	}
}
package self.learn.setdemo;

import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
	public static void main(String[] args) {
		Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
		allSet.add(new Person("张三", 30));
		allSet.add(new Person("李四", 31));
		allSet.add(new Person("王五", 32));              
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人		
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人
		allSet.add(new Person("赵六", 33));
		allSet.add(new Person("孙七", 33));               // 年龄重复
		System.out.println(allSet);    // 输出集合对象,调用 toString()方法
	}
}

运行结果截图:

在这里插入图片描述
   从程序的运行结果 中可以发现,重复的“王五” 对象只有一个,“赵六”和“孙七” 的数据中姓名并不重复,只有年龄重复了,但是“孙七” 却没有加入到集合中。这是因为采用了比较器造成的,比较器操作的时候如果某个属性没有进行比较的话,则也会认为是同一个对象,所以此时应该将 Person 类中的 compareTo 方法中增加按姓名比较。

修改 person 的比较器后

package self.learn.setdemo;

public class Person implements Comparable<Person>{             // 定义 Person 类实现比较器
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {                               // 覆写 toString() 方法
		return "Person [name=" + name + ", age=" + age + "]";
	}	
	public int compareTo(Person per) {         // 覆写 compareTo() 方法,指定排序规则
		if(this.age > per.age) {               // 通过年龄排序
			return 1;
		}else if (this.age < per.age) {
			return -1;
		}else {
			return this.name.compareTo(per.name); // 增加字符串的比较
		}
	}
}
package self.learn.setdemo;

import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
	public static void main(String[] args) {
		Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
		allSet.add(new Person("张三", 30));
		allSet.add(new Person("李四", 31));
		allSet.add(new Person("王五", 32));              
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人		
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人
		allSet.add(new Person("赵六", 33));
		allSet.add(new Person("孙七", 33));               // 年龄重复
		System.out.println(allSet);    // 输出集合对象,调用 toString()方法
	}
}
运行结果截图:

在这里插入图片描述
   上面的程序运行结果中“孙七”出现了,而且去掉了重复的内容,但是此时的重复内容并不是真正意义的去掉。因为此时靠的是 Comparable 接口完成的,如果现在换成 HashSet 也会出现重复的内容。所以想要真正的重复内容去掉,必须深入研究 Object 类。

package self.learn.setdemo;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
	public static void main(String[] args) {
		Set<Person> allSet = new HashSet<Person>(); // 实例 Set 集合类
		allSet.add(new Person("张三", 30));
		allSet.add(new Person("李四", 31));
		allSet.add(new Person("王五", 32));              
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人		
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人
		allSet.add(new Person("赵六", 33));
		allSet.add(new Person("孙七", 33));               // 年龄重复
		System.out.println(allSet);    // 输出集合对象,调用 toString()方法
	}
}
运行结果截图:

在这里插入图片描述
   从程序的运行结果可以发现,“王五”的内容重复了,也就是说,此时的程序并没有像 Set 接口规定的那样是不允许出现重发元素的。如果此时想要去掉重复的元素,则必须首先进行对象是否重复的判断,要想进行这样的判断则一个类中就必须覆写 Object 类中的 equals()方法,才能完成对象是否相等的判断,但是只覆写 equals()方法是不够的,还需覆写 hashCode() 方法,此方法表示一个哈希码,可以将类中的全部属性进行适当的计算,以求出一个不会重复的哈希码。

通过 Object 类去掉重复元素,通过实现 Comparable 接口排序

package self.learn.setdemo;

public class Person implements Comparable<Person> {             // 定义 Person 类实现比较器
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}		
	public boolean equals(Object obj) {       // 覆写 equals() 方法
		if(this == obj) {                     // 地址相等
			return true;                      // 是同一对象
		}
		if(!(obj instanceof Person)) {        // 传递进来的不是本类对象
			return false;                     // 不是同一对象
		}
		Person p = (Person)obj;               // 进行向下转型
		if(this.name.equals(p.name)&&this.age == p.age) {  // 属性依次比较
			return true;                                   // 全部属性相等,是同一对象
		}else {                  
			return true;                                   // 属性不相等,不是同一对象
		}		
	}
	
	public int hashCode() {          // 覆写 hashCode() 方法
		return this.name.hashCode()*this.age;  // 指定编码公式
	}
	
	public int compareTo(Person per) {         // 覆写 compareTo() 方法,指定排序规则
		if(this.age > per.age) {               // 通过年龄排序
			return 1;
		}else if (this.age < per.age) {
			return -1;
		}else {
			return this.name.compareTo(per.name); // 增加字符串的比较
		}
	}
	
	@Override
	public String toString() {                               // 覆写 toString() 方法
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

package self.learn.setdemo;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
	public static void main(String[] args) {
		//Set<Person> allSet = new TreeSet<Person>(); // 实例 Set 集合类
		Set<Person> allSet = new HashSet<Person>(); 
		allSet.add(new Person("张三", 30));
		allSet.add(new Person("李四", 31));
		allSet.add(new Person("王五", 32));              
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人		
		allSet.add(new Person("王五", 32));               // 重复元素,不能加人
		allSet.add(new Person("赵六", 33));
		allSet.add(new Person("孙七", 33));               // 年龄重复
		System.out.println(allSet);    // 输出集合对象,调用 toString()方法
	}
}
HashSet 子类运行结果截图:没有重复元素

在这里插入图片描述

TreeSet 子类运行结果截图:没有重复元素 及排序成功

在这里插入图片描述

提示: Object 类总结

   经过以上的程序代码练习,Object 类中的全部方法就介绍完了,相信读者已经清楚地知道各个方法的作用,在实际的开发中经常会碰到区分同一对象的问题,所以,一个完整的类最好覆写 Object 类的 hashCode()、equals()、toString() 3 个方法。

3. SortedSet 接口

   从 TreeSet 类的定义中可以发现,TreeSet 里实现了 SortedSet 接口,此接口主要是用于排序操作的,即实现此接口的子类都属于排序的子类。SortedSet 接口定义如下:

public interface SortedSet<E> extends Set<E>

SortedSet 接口中定义的方法如下表:

序号方法类型描述
1public Comparator<? super E> comparator()普通返回与排序有关联的比较器
2public E first()普通返回集合中的第一个元素
3public SortedSet<E> headSet(E toElement)普通返回从开始到指定的元素的集合
4public E last()普通返回最后一个元素
5public SortedSet<E> subSet(E fromElement,E toElement)普通返回指定对象间的元素
6public SortedSet<E> tailSet(E fromElement)普通从指定元素到最后

3.1 验证 SortedSet 接口

package self.learn.setdemo;

import java.util.SortedSet;
import java.util.TreeSet;
public class SetDemo {
	public static void main(String[] args) {
		SortedSet<String> allSet = new TreeSet<String>(); // 实例 Set 集合类		
		allSet.add("A");
		allSet.add("B");
		allSet.add("C");              
		allSet.add("C");               // 重复元素,不能加入	
		allSet.add("C");               // 重复元素,不能加入
		allSet.add("D");
		allSet.add("E");               // 年龄重复
		System.out.println("第一个元素:"+allSet.first());    // 输出集合对象,调用 toString()方法
		System.out.println("第一个元素:"+allSet.last());
		System.out.println("headSet 元素:"+allSet.headSet("C"));
		System.out.println("tailSet 元素:"+allSet.tailSet("C"));
		System.out.println("subSet 元素:"+allSet.subSet("A","D"));
	}
}
运行结果截图:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值