Set是一个接口
Set接口和List接口的区别在哪?
List中一些方法(上图中红色标记),在Set中没有,这些方法都与index(位置/索引/下标)有关。 由于List “有序”列表,第一次添加的元素,index的值为0,第二次添加的元素,index的值为1.......... 但是Set “无序”集合,在无序集合中,不存在“下标/索引”的概念,也就是没有第一个元素、第二元素 之类的说法。
set的特点
- 无序的,
- 不可以添加重复
Set接口有很多实现类,
如HashSet、TreeSet、LinkedHashSet.....等,其中最常用的就是HashSet
方法:
clear ()
删除集合所有元素
remove
()
删除集合元素
add ()
添加元素
clear ()
清除所有
contains()
判断是否包含
isEmpty()
是否为空
remove()
删除元素
size()
元素个数
HashSet<String> s = new HashSet<String>();//创建对象
s.add("信阳毛尖出名");
s.add("南阳");
s.add("洛阳");
s.add("安阳");
s.add("淮阳");
System.out.println(s);//Set是无序的 (没有固定的顺序,打印的顺序和插入的顺序不同,并且是会变的)
s.remove("安阳");
System.out.println(s);
System.out.println(s.contains("洛阳"));
//请判断set集合中是否有长度大于4的字符串
//set的遍历 foreach
for(String t : s){
if(t.length()>4){
System.out.println(true);
}
}
不只是Set可以使用foreach遍历,List、数组这些集合形式都可以使用。
foreach删除陷阱
在遍历Set时,改变了Set集合的结构,就会导致程序报错。
要求,将set中的所有包含信阳的元素删除:
代码如下:
HashSet<String> s = new HashSet<String>();//创建对象
s.add("信阳毛尖出名");
s.add("南阳");
s.add("信阳");
//set的遍历 foreach
for(String t : s){
if(t.indexOf("信阳")>=0){
s.remove(t);
}
}
System.out.println(s);
}
报错是java.util.ConcurrentModificationException,foreach中改变了集合的结构
因为在foreach遍历s时,改变了s的结构
解决:克隆一个新的列表
正确代码
public static void main(String[] args) {
HashSet<String> s = new HashSet<String>();//创建对象
s.add("信阳毛尖出名");
s.add("南阳");
s.add("信阳");
//1 复制set的方法1:克隆
HashSet<String> s1 = (HashSet<String>) s.clone();
HashSet<String> s2 = new HashSet<String>();
//复制set的方法2:addAll 添加所有
s2.addAll(s);
//set的遍历 foreach
for(String t : s1){
if(t.indexOf("信阳")>=0){
s.remove(t);
}
}
System.out.println(s);
}
HashSet的存储机制:
toString equals Object类的hashCode方法:该方法默认返回一个对象的地址(准确来说是地址的变形)
HashSet是一个二维数组/链表的结构,有一个原始的长度为16的数组,该数组的每个元素,都指向一个新 的数组来存放元素。 当Set中放入一个元素s时,会经过以下步骤:
1)获取s的hashCode值
2)将值对16求余,得到的结果就是空间编号
3)该元素,就是要准备放入编号2对应的数组空间中
4)在放入之前,会将s与空间2中的已有元素进行equals比较。如果相等,就认为元素重复,放弃插入;如 果都不相等,那么就把s放入到数组中
重写hashCode方法
String a = new String("123");
String b = new String("123");
但是a与b的hashCode值却相同
看到a与b的内存地址不同,但是hashCode值却相同
Set集合中,存放了两个值相同的字符串。
所以,为了避免这种结果的发生,必须保证两个值相同的字符串,他们的hashCode值也相同。
String为了实现这个目的,重写了hashCode方法,返回值不再跟对象的地址有关,而是跟对象的值有关。
Set拓展
实现HashSet中只有一个学生信息(保证学生姓名和年龄不能重复)
1)保证两个值(姓名/年龄)相同的学生,他们的hashCode值也相同 (重写hashCode方法)
2) 保证两个值(姓名/年龄)相同的学生,他们的equals方法返回true(重写equals方法)
代码如下
public class Student {
String name;
int age;
@Override
public String toString() {
// TODO Auto-generated method stub
return name+"-"+age;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return 0;
}
@Override
public boolean equals(Object obj) {
Student s = (Student) obj;
if(this.name.equals(s.name) && this.age==s.age){
return true;
}
return false;
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "jack";
s1.age = 18;
Student s2 = new Student();
s2.name = "jack";
s2.age = 18;
HashSet<Student> hs = new HashSet<Student>();
hs.add(s1);
hs.add(s2);
System.out.println(hs);
}
}
set注重独一无的性质,体系集合用于存储无序(存入和取出的顺序不一定相同)的元素,值不能重复。对象的相等性本质是对象hashCode值(Java是依据对象的内存地址计算的此序号)判断的,如果想要让两个对象失视为相等的,就必须Objiet的hashCode方法和equals方法。
HashSet特点:
排列无序,不可重复
底层使用Hash表实现
内部是HashMap
存取速度快
TreeSet特点:
底层使用二叉树实现
排序存储
内部是TreeMap的SortedSet
排列无序,不可重复
LinkedHashSet 特点:
内部是LinkedHashMap
采用hash表存储,并且双向链表记录插入顺序