回顾问题:
上一期我们讲了list集合,它的一个显著特点就是读取速度快,元素可重复,可以自动扩容!!!有一个问题,在上一期中,我们使用foreach去删除元素是会发现在两个相邻的同一样的元素中会发生执行错误,如果我们没有重复相邻的两个元素,再去删除,还是会有问题,但我们如果刚好删除的是倒数第二个元素,就可以正常删除,这是为什么呢,我们一起来看看。
下面的图都为源码:
大家首先看到这里的这个方法,在使用list进行foreach进行删除的时候我们就会进入到这里来,然后进行删除,那具体是怎么样的呢,其实在使用foreach进行删除的时候是调用的一个iterator来进行的,这个迭代器就是我们的array list的内部类中的iterator,它进入到这里之后会调用hasnext方法进行cursor进行一个和集合元素的长度进行比较,如果不相等就还有元素,然后接着往下走,调用next方法先去进行一个检查维护,那这个modCount是什么呢,它其实是我们的集合的大小,当我们集合元素大小发生变化是就会更着改变,例如,如果集合原本大小为5,此时插入一个元素,就变成了6.modCount也变成了6,减少一个变成4,modCount也变成了4。所以我们就会发现,在我们用foreach进行list的删除时这个检查约束过不不了就直接抛出异常了,而为什么倒数第二可以呢,以为删除它就变成倒数第一了,来不及检查约束就已经删除了,其实这也时一个特性,但它本质是一个bug。
检查方法
Set集合
特点:
1. 特点:无序,不重复
2. 遍历:foreach,迭代器
3. 扩容: 初始容量16,负载因子0.75,扩容增量1倍
实现类
HashSet
特点:
1.它存储唯一元素并允许空值,依据对象的hashcode来确定该元素是否存在
2. 由HashMap支持
3. 不保持插入顺序
4. 非线程安全
5. 性能参数:初始容量,负载因子,默认值: 初始容量16,负载因子0.75,示例:new HashSet<>(20, 0.5f);
实例
List<Integer> list = new ArrayList<Integer>();
private Set<Integer> set = new HashSet<Integer>();
@Before
public void setup() {
list.add(1);
list.add(4);
list.add(4);
list.add(8);
list.add(8);
list.add(12);
list.add(16);
set.add(1);
set.add(2);
set.add(3);
set.add(3);
set.add(4);
set.add(4);
set.add(5);
}
@Test
public void setdome01() {
List<Integer> temp = new ArrayList<Integer>(new HashSet<Integer>(list));
System.out.println(temp);
// 结果:[16,1,4,8,12],hashset直接帮我们去重复了,因为set集合不可以出现重复的
// 除了这种方法还有list的迭代器的去重复,还有最为死的for遍历,一个一个做判断删除
}
// HashSet其实就是一个皮包公司,都是委托
@Test
public void setdome02() {
for (Integer ui : set) {
System.out.println(ui);
}
// 结果:12345;
// 可以看到,去重复和排序了
}
@Test
public void setdome03() {
Iterator<Integer> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 结果:12345
}
// 可以看到,我们插入的到顺序是无序的,但我们输出的却是有序的,而set是无序的,
// 那为什么输出的有序呢,那是因为set的无序指的是插入的顺序是无序 ,而这的有序
// 是因为hashset的有序,我们常说的有序是指插入的顺序是有序的
TreeSet
1. 是一个包含有序的且没有重复元素的集合
2. 作用是提供有序的Set集合,自然排序或者根据提供的Comparator进行排序
3. TreeSet是基于TreeMap实现的
比较器
1.默认自然排序
1. 默认自然排序
数据准备
ts = new TreeSet<>();
ts.add(1);
ts.add(8);
ts.add(7);
ts.add(2);
ts.add(4);
ts.add(6);
ts.add(3);
@Test
public void setdome04() {
Set<Student> set = new HashSet<Student>();
set.add(new Student(1, "张三", 18));
set.add(new Student(2, "李四", 19));
set.add(new Student(3, "李凝", 20));
set.add(new Student(4, "王三", 21));
set.add(new Student(4, "王三", 21));
set.add(new Student(5,"二虎",26));
for (Student student : set) {
System.out.println(student);
}
}
2.自定义排序
--1. 较器通过构造函数传入比
1. 较器通过构造函数传入比
TreeSet<Integer> tset = new TreeSet<Integer>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2 - o1;
}
});
2. 2. 实现排序接口
public class Student implements Comparable<Student>{
private Integer sid;
private String name;
private int age;
@Override
public int compareTo(Student o) {
// TODO Auto-generated method stub
return o.getAge() - this.getAge();
}
}
Student实体类
package com.zking.test;
public class Student implements Comparable<Student> {
private Integer sid;
private String sname;
private int age;
public Student(Integer sid, String sname, int age) {
super();
this.sid = sid;
this.sname = sname;
this.age = age;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((sid == null) ? 0 : sid.hashCode());
result = prime * result + ((sname == null) ? 0 : sname.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 != other.age)
return false;
if (sid == null) {
if (other.sid != null)
return false;
} else if (!sid.equals(other.sid))
return false;
if (sname == null) {
if (other.sname != null)
return false;
} else if (!sname.equals(other.sname))
return false;
return true;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
}
@Override
public int compareTo(Student o) {
if (this.getAge() - o.getAge() == 0) {
return this.getSid() - o.getSid();
}
return this.getAge() - o.getAge();
}
}
总结:
set集合元素无放入顺序,且不可重复(注意:元素虽然无放入顺序,但是元素在Set中的位置是由该元素的HashCode决定的,其位置是固定的)。List支持for循环,也就是通过下标来遍历,也可以用迭代器,但是Set只能用迭代器,因为他无序,无法使用下标取值;Set:检索元素效率低,删除和插入效率高,插入和删除不会引起元素位置改变。