Set概述、哈希值概述、HashSet概述(附加存储学生对象案例)、LinkedHashSet概述、TreeSet概述、Comparable和Comparator(附存储不重复随机数案例)
目录
Set集合概述特点
所在包:java.util
public interface Set<E>
extends Collection<E>
//Set作为接口不能直接进行实例化,必须找到Set相应的实现类。
特点:不包含重复元素的集合
Set集合中没有带索引的方法,所以不能使用普通for循环遍历
注意:
数组用Arrays类方法
集合用Collections类方法
实现代码案例:
Set<String> s = new HashSet<String>();
s.add("hello");
s.add("world");
s.add("java");
for(String c:s){
System.out.print(c); //Set对集合的迭代顺序不做任何保证
}
输出结果:
world
java
hello
哈希值概述特点
哈希值:
- 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
- Object类中有hashCode()可以获取对象的哈希值:public Object hashCode()返回对象的哈希值
对象的哈希值特点:
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下(没有在类中重写hashCode()方法的情况下),不同对象的哈希值是不同的,重写hashCode()方法可以让不同对象的哈希值相同
HashSet< E >
所在包:java.util
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>,Cloneable,Serializable
特点:
- 底层数据结构是哈希表
- 对集合的迭代顺序不做任何保证,也就是说不保证存储数据和取出数据的元素顺序一致
- 没有带索引的方法,所以不能用普通for循环遍历
- 由于是Set集合,所以在集合中不包含重复的元素
初始化格式:
HashSet<E> hs=new HashSet<E>();
注意:HashSet继承了Set接口所以可以用add、remove、clear、isEmpty、iterator(获取迭代器)等方法
HashSet是如何保证元素不相同的:
- 存入的元素和已经存入的元素比较哈希值
- 如果哈希值不同,则继续向下执行,把元素添加到集合
- 如果哈希值相同,则调用对象的equals方法进一步比较元素值是否相同
- 如果equals返回false,则会继续向下执行,把元素添加到集合
- 如果equals返回true,则说明元素重复,不会向下执行
图例:
存储学生对象案例:
public static void main(String[] args){
HashSet<Student> h = new HashSet<Student>();
Student s1=new Student("张三",123,99);
Student s2=new Student("李四",456,98);
Student s3=new Student("王五",789,70);
Student s4=new Student("张三",123,99);
h.add(s1);
h.add(s2);
h.add(s3);
h.add(s4);
for(Students:h){
System.out.println(s.getName()+","+s.getId()+","+s.getScore());
}
}
-------------------------------------------------------------------------------------------------
输出结果: // 如果在学生类中重写了方法equals()和hashCode()则不会出现重复对象类型
张三,123,99
李四,456,98
张三,123,99
王五,789,70
注意:继承Set接口下的HashSet输出结果不能保证输入和输出的顺序相同
LinkedHashSet< E >
所在包:java.util
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>,Cloneable,Serializable
LinkedHashSet特点:
- 底层逻辑由哈希表和链表实现的Set接口,具有可预测的迭代次序(存储顺序和输出顺序相同)
- 由链表保证元素的有序性,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素的唯一性,也就是说表中没有重复的元素
代码示例:
LinkedHashSet<String> link = new LinkedHashSet<String>();
link.add("world");
link.add("hello");
link.add("java");
for(String s:link){
System.out.print(s);
}
输出结果:
World
Hello
java
TreeSet
所在包:java.util
public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>,Cloneable,Serializable
TreeSet集合特点:
- 元素有序,这里的排序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方法取决于
构造方法名 | 作用 |
---|---|
TreeSet() | 根据其元素的自然排序进行排序(例:Integer数字按照从小到大的排序顺序) |
TreeSet(Comparator comparator) | 根据指定的比较器进行指定的排序 |
代码实例:
Public static void main(String[] args){
TreeSet<Integer> tree = new TreeSet<Integer>();
//注意集合体只能存储引用类型,如int的包装类类型Integer。
tree.add(9);
tree.add(8);
tree.add(7);
for(Integeri:tree){
System.out.print(i);
}
}
输出结果:
9
8
7
自然排序Comparable的使用范例
案例要求:存储学生对象并遍历,创建TreeSet集合使用无参构造方法。按照年龄从小到大排序,年龄相同时,按照姓名字母顺序排序
代码实现:
在Student类中必须要实现Comparable<E>接口并在类中重写compareTo()方法
public class Student implements Comparable<Student>{
//定义学生类 并实现Comparable接口(自然排序接口)
… …
@Override //重写compareTo方法
public int compareTo(Student s){
int nums = this.Id-s.Id;
//在add方法中调用S2.compareTo(S1); 所以this.Id是指S2中的Id,而s.Id则是S1中的Id
//这是一个主要判断排序条件--按照id大小排序
int temp = nums == 0 ? this.Name.compareTo(s.Name) : nums;//nums==0则说明年龄相同,比较姓名
//若姓名相同则返回nums给temp
//这是一个次要判断排序条件--按照姓名顺序排序
return temp;
}
}
public class static void main(String[] args){
TreeSet<Student> t = new TreeSet<Student>();
Student s1=new Student("zs",4,99);
Student s2=new Student("ls",3,98);
Student s3=new Student("ww",1,99);
Student s4=new Student("lw",2,97);
Student s5=new Student("ll",2,99);
t.add(s1);
t.add(s2);
t.add(s3);
t.add(s4);
t.add(s5);
for(Students:t){
System.out.println(s.getName()+","+s.getId()+","+s.getScore());
}
}
输出结果:
ww,1,99
ll,2,99
lw,2,97
ls,3,98
zs,4,99
-------------------------------------------------------------------------------------------------
小结:
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的,自然排序
就是让接口所属的类实现Comparable<E>接口,重写compareTo(T o)方法,重写方法时,一定
要注意排序规则必须按照要求的主要条件和次要条件来写。
Comparator的使用范例
案例要求:存储学生对象并遍历,创建TreeSet集合使用带参构造方法。要求按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
代码实现:
public class Student{
… …} //用Comparator比较器时可以不用在元素所属的类中实现Comparable接口
Public class static void main(String[] agrs){
TreeSet<Student> t = new TreeSet<Student>(new Comparator<Student>(){
//利用匿名内部类方法创建Comparator<E>比较器作为TreeSet<E>(Comparator comparator)的参数
@override
public int compare(Student s1,Student s2){
//注意是重写compare方法不是compareTo
int nums = s1.getId() - s2.getId();
//这是一个主要判断排序条件--按照id大小排序
int temp = nums == 0 ? s1.getName().compareTo(s2.getName()) : nums;
//这是一个次要判断排序条件--按照姓名顺序排序
return temp;
}
})
… … //与自然排序Comparable代码相同
}
-------------------------------------------------------------------------------------------------
小结:
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的比较器排
序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法,重写
方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
不重复随机数案例
要求:编写一个程序,获得10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
代码实现:
public static void main(String[]args){
Set<Integer> t = new TreeSet<Integer>();
//利用TreeSet给集合Set进行实例化,可让集合中的Integer元素实现自动从小到大排序
Random r = new Random(); //定义随机数类
while(t.size() < 10){
int number=r.nextInt(20)+1;
t.add(number);
}
for(Integer i : t){
System.out.print(I + " ");
}
}
注意:Set集合是接口不能直接进行实例化,可以用TreeSet、LinkedHashSet、HashSet等类进行实例化。