目录
第一题
代码演示:
思路分析:
HashSet去重机制
//HashSet实现去重是因为 在底层会根据你传入的key计算一个hash值,
//会先计算一个你在table表中存放的位置 ,如果该位置还没有其他元素添加则直接加入
//如果已经有了会先进行判断,如果你的加入的对象的hash值和当前位置的hash值相同
//或者是同一个对象,或者是他们的equals相同就不进行添加
//并且底层还会判断当前是不是红黑树,如果是就按照红黑树的方法进行
//如果是一个链表 就依次循化遍历,期间如果发现相同的就直接break,如果没有发现相同的就直接加入到链表的最后一个位置
TreeSet去重机制
//TreeSet去重机制:如果我们传入了comparator匿名对象,那么底层会使用compare方法进行比较 //如果返回0就不会加入
//如果没有传入对象,直接使用默认的那么会调用
// Comparable<? super K> k = (Comparable<? super K>) key;
//就是以你添加的对象实现的Comparable接口的compareTo去重.如果返回0也加入不成功】
//举例 //如果我们没有传入对象,并且传入的类型还是一个String类型那么在执行到Comparable<? super K> k = (Comparable<? super K>) key;这句话时 //会动态绑定到String的compareTo方法因为String实现了Comparable这个接口
package idea.chapter14.homework;
public class Homework04 {
public static void main(String[] args) {
//HashSet实现去重是因为 在底层会根据你传入的key计算一个hash值,
//会先计算一个你在table表中存放的位置 ,如果该位置还没有其他元素添加则直接加入
//如果已经有了会先进行判断,如果你的加入的对象的hash值和当前位置的hash值相同
//或者是同一个对象,或者是他们的equals相同就不进行添加
//并且底层还会判断当前是不是红黑树,如果是就按照红黑树的方法进行
//如果是一个链表 就依次循化遍历,期间如果发现相同的就直接break,如果没有发现相同的就直接加入到链表的最后一个位置
//TreeSet去重机制:如果我们传入了comparator匿名对象,那么底层会使用compare方法进行比较
//如果返回0就不会加入
//如果没有传入对象,直接使用默认的那么会调用
// Comparable<? super K> k = (Comparable<? super K>) key;
//就是以你添加的对象实现的Comparable接口的compareTo去重.如果返回0也加入不成功
//举例
//如果我们没有传入对象,并且传入的类型还是一个String类型那么在执行到Comparable<? super K> k = (Comparable<? super K>) key;这句话时
//会动态绑定到String的compareTo方法因为String实现了Comparable这个接口
//看源码
/*
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
//这里会动态绑定到我们自己写的匿名内部类
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
这里就是添加不成功的情况,返回0,就是添加失败
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
*/
}
}
第二题
代码演示:
思路分析:
因为我们的Person类没有实现Comparable接口,因此在底层的时候,会根据你传入进来的对象尝试将他转成Comparable这个类型,如果传入的对象没有实现改接口那么就报类型转换异常,
如果不想出错,让Person类实现Comparable接口,并重写compareto方法即可
package idea.chapter14.homework;
import java.util.TreeSet;
@SuppressWarnings({"all"})
public class Homework05 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
//分析源码
//add 方法,因为 TreeSet() 构造器没有传入Comparator接口的匿名内部类
//所以在底层会去调用你传入对象的Comparable接口的compareTo方法 但是Person类并没有实现Comparable接口
//因此,会尝试把一个Person转成(Comparable<? super K>)所有会报类型转换异常ClassCastException.
// Comparable<? super K> k = (Comparable<? super K>) key;
treeSet.add(new Person());//ClassCastException.
treeSet.add(new Person());//ClassCastException.
treeSet.add(new Person());//ClassCastException.
treeSet.add(new Person());//ClassCastException.
treeSet.add(new Person());//ClassCastException.
System.out.println(treeSet);
//看源码
/*
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;//如果没有传入匿名对象那么此时comparator为null所以会不会进入下面的if语句
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;//会直接执行到这里来 会根据你传入进来的对象尝试将他转成Comparable这个类型,如果传入的对象没有实现改接口那么就报类型转换异常
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
*/
}
}
class Person implements Comparable{
//如果想不报错那么就需要让Person类去实现Comparable接口
//因为我们写的时return 0 因此只能加入一个,如果加入多个会加入不进去,因为返回0就是不加入
@Override
public int compareTo(Object o) {
return 0;
}
}
第三题
代码演示:
思路分析:
/* 因为Person类重写了equals方法和hashCode方法,HashSet底层时HashMap在添加元素时 会先计算你的hash值,在转换成索引值,也就是在table表中的位置加入,如果当前位置为空 那么就直接加入,如果不为空则进行后续的比较 p1 和 p2虽然重写了hashcode方法和equals方法,但是他们的内容不相同,因此计算出来的 索引值也不会相同,所以能够成功的添加进去 在执行p1.name = "CC";时会把p1里的AA改成CC 这时set.remove(p1);想再去删除p1时 是根据当前的id 1001和当前的name CC来计算 ,计算过后等到的索引值会在table数组的其他位置 因此删除不成功 在执行set.add(new Person(1001,"CC"));这句话时,表中已经有了一个 id 为1001 name 为CC 虽然他们的id和name都相同,但是最后添加的那个对象都是重写new出来的,他们的hash值不同 也不是同一个对象 所以可以 添加成功 可以通过Debug查看 set.add(new Person(1001,"AA"));这句话和上面相同 */
package idea.chapter14.homework;
import java.util.HashSet;
import java.util.Objects;
@SuppressWarnings({"all"})
public class Homework06 {
public static void main(String[] args) {
HashSet set = new HashSet();//ok
Person p1 = new Person(1001,"AA");//ok
Person p2 = new Person(1002,"BB");//ok
set.add(p1);//ok
set.add(p2);//ok
p1.name = "CC";
//这里会删除失败,因为删除是根据你当前的id 和 name 来计算的 而你在删除之前把name的值修改了,那么在删除的时候,需要重新计算hash的值,得到的位置就不再是之前的位置了
set.remove(p1);
System.out.println(set);//2
//这里可以加入成功,因为是根据当前的id1001 和 name CC 来计算的
set.add(new Person(1001,"CC"));
System.out.println(set);//3
//这里也可以加入,和我们第一个加入的元素p1一样,他们的id 都是1001 name也是AA 所以计算出来的hash值肯定是相同的,因为我们之前把p1的值修改了,修改成了CC 所以这里会把值直接挂在p1的后面
set.add(new Person(1001,"AA"));
System.out.println(set);//4
/*
因为Person类重写了equals方法和hashCode方法,HashSet底层时HashMap在添加元素时
会先计算你的hash值,在转换成索引值,也就是在table表中的位置加入,如果当前位置为空
那么就直接加入,如果不为空则进行后续的比较
p1 和 p2虽然重写了hashcode方法和equals方法,但是他们的内容不相同,因此计算出来的
索引值也不会相同,所以能够成功的添加进去
在执行p1.name = "CC";时会把p1里的AA改成CC 这时set.remove(p1);想再去删除p1时
是根据当前的id 1001和当前的name CC来计算 ,计算过后等到的索引值会在table数组的其他位置
因此删除不成功
在执行set.add(new Person(1001,"CC"));这句话时,表中已经有了一个 id 为1001 name 为CC
虽然他们的id和name都相同,但是最后添加的那个对象都是重写new出来的,他们的hash值不同 也不是同一个对象
所以可以 添加成功 可以通过Debug查看
set.add(new Person(1001,"AA"));这句话和上面相同
*/
}
}
class Person {
public String name;
public int id;
public Person(int id, String name) {
this.name = name;
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, id);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
Vector和ArrayList的比较
底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 如果使用有参构造器按照1.5倍扩容,如果是无参构造器。第一次扩容10.第二次开始按照1.5倍 |
Vector | 可变数组Object[] | jdk1.0 | 安全,效率不高 | 如果是无参,默认是10,满后,按照2倍扩容。如果指定大小创建Vector,则每次按照2倍扩容 |