一、Set集合
set接口是Collection的子接口,不提供额外的方法,但在使用规范比Collection更加严格
不允许包含重复的元素
遍历可以通过Foreach和Iterator
set接口常用实现类有:HashSet、TreeSet、LinkedHashSet
set集合无序存储的原因
set集合的底层实现实际上是采用哈希表存储元素
JDK1.8之前:哈希表 = 数组 + 链表 + ( 哈希算法 )
JDK1.8之后:哈希表 = 数组 + 链表 + 红黑树 + ( 哈希算法 )
哈希算法简述
数组体现:
首先利用hashcode得到对象的哈希值
将其哈希值对底层数组长度求余得到该对象对应的底层数组的存储索引位置
将元素放入底层数组
链表体现:
当在底层数组中的某一位置已经有了元素占据,则再次需要有元素放入该位置时,将会在该位置形成链表
二、HashSet集合
HashSet概述
java.util.HashSet的底层实现实际是 java.util.HashMap,而HashMap的底层是一个哈希表。
特点
HashSet底层使用的是HashMap
不能保证元素的顺序,元素是无序的,不能有重复的元素
集合元素值允许为null
HashSet线程不安全
HashSet常用方法
①:add(Object o):向Set集合中添加元素,不允许添加重复数据。
②:size():返回Set集合中的元素个数
HashSet<String> set = new HashSet<String>(); //调用HashSet无参构造方法——>创建HashMap对象并给map全局变量。
set.add("青城");
set.add("博雅");
set.add("青城1");
set.add("青城1");
System.out.println(set);
System.out.println(set.size());
运行结果:
注意:不会按照保存的顺序存储数据(顺序不定),遍历时不能保证下次结果和上次相同。且向HashSet集合中添加元素,HashSet add方法实质是map全局变量调用了put方法,将数据存到了key,因为HashMap的 key不允许,所以HashSet添加的元素也不允许重复。
③.remove(Object o): 删除Set集合中的obj对象,删除成功返回true,否则返回false。
④.isEmpty():如果Set不包含元素,则返回 true。
HashSet<String> set = new HashSet<String>();
set.add("青城");
set.add("博雅");
System.out.println(set.isEmpty());
System.out.println(set.remove("博雅"));
System.out.println(set);
运行结果:
⑤.clear(): 移除此Set中的所有元素。
HashSet<String> set = new HashSet<String>();
set.add("青城");
set.add("博雅");
System.out.println(set);
set.clear();
System.out.println(set);
运行结果:
⑥.iterator():返回在此Set中的元素上进行迭代的迭代器。
HashSet<String> set = new HashSet<String>();
set.add("青城");
set.add("博雅");
Iterator<String> ite =set.iterator();
while(ite.hasNext())
{
System.out.println(ite.next());
}
运行结果:
⑦.contains(Object o):判断集合中是否包含obj元素。
HashSet<String> set = new HashSet<String>();
set.add("青城");
set.add("博雅");
System.out.println(set.contains("青城"));
运行结果:true
⑧:加强for循环(foreach)遍历Set集合:
HashSet<String> set = new HashSet<String>();
set.add("青城");
set.add("博雅");
for (String name : set) { //使用foreach进行遍历。
System.out.println(name);
}
运行结果:
三、LinkedHashSet集合
LinkedHashSet特点
LinkedHashSet底层使用的是HashSet,同时使用链表维护元素的插入顺序
元袁有序且唯一,链表保证元袁有序
哈希表保证元素唯一线程不安全
四、TreeSet集合
TreeSet特点
TreeSet其内部使用的是TreeMap,TreeMap是基于红黑树实现的
插入数据内部有两种排序方法
自然排序(默认)
定制排序
无序:TreeSet会对插入的数据排序,所以输入顺序和输出顺序不—致
值不能为null
值唯一
线程不安全
TreeSet基本使用
①.插入是按字典序排序的
TreeSet ts=new TreeSet();
ts.add("agg");
ts.add("abcd");
ts.add("ffas");
Iterator it=ts.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
输出 : 按照字典序排序的方式进行排序
abcd
agg
ffas
②.如果插入的是自定义对象 需要让类实现 Comparable 接口并且必须要重写compareTo
class Person implements Comparable{
String name;
int age;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
@Override
public int compareTo(Object o) {
Person p=(Person)o;
//先对姓名字典序比较 如果相同 比较年龄
if(this.name.compareTo(p.name)!=0) {
return this.name.compareTo(p.name);
}
else
{
if(this.age>p.age) return 1;
else if(this.age<p.age) return -1;
}
return 0;
}
}
public class Test {
public static void main(String args[])
{
TreeSet ts=new TreeSet();
ts.add(new Person("agg",21));
ts.add(new Person("abcd",12));
ts.add(new Person("ffas",8));
ts.add(new Person("agg",12));
Iterator it=ts.iterator();
while(it.hasNext())
{
Person p=(Person)it.next();
System.out.println(p.name+":"+p.age);
}
}
}
输出:
abcd:12
agg:12
agg:21
ffas:8
五、HashSet、LinkedHashSet、TreeSet的使用场景
HashSet:HashSet的性能基本上比LinkedHashSet和TreeSet要好,特别是添加和查询,这也是用的最多的两个操作
LinkedHashSet:LinkedHashSet的查询稍慢一些,但是他可以维持元素的添加顺序。所以只有要求当插入顺序和取出顺序一致的时候才使用LinkedHashSet。
TreeSet:只有在需要对元素进行排序时使用
六、List和Set集合的区别
七、为什么要重写equals和hashCode方法
equals方法是做什么的?
不对equals方法进行重写时,默认调用object的equals方法。而equals方法实际上是对两个对象的的地址进行判断,判断两个对象地址是否相同
什么是hashcode?
hashcode也叫做哈希值,每一个对象都会有一个哈希值。但要注意区分哈希值并不代表该对象的存储地址,哈希值的组成是由该对象的value字符串决定,与地址无关。
从抽象角度来讲,哈希值是一个名词。每个对象会有自己的存储地址,而这个“存储地址”也是一个名词,可以看成是这个对象的一个特性,而哈希值也是如此,调用hashCode()方法即可得到对象的哈希值。在集合的应用中,hashcode也代表了该对象在此集合的存储位置
为什么要重写equals和hashCode方法?
两个对象比较时如果equals返回true,则hashCode方法返回值必须相同
如果不重写hashCode方法则两个对象的hashCode方法返回的依然是两个对象的地址值,此结果必然不同。也就出现了equals相等但是hashCode方法返回值不同的情况
重写equals就必须重写hashCode方法