为什么要用HashSet呢,因为要防止重复!为什么使用TreeSet呢,因为HashSet不可以排序!
在list的基础上,添加如下代码:
HashSet<Song> songSet=new HashSet<Song>();
//如果使用TreeSet,则为:
//TreeSet<Song> songSet=new TreeSet<Song>();
songSet.addAll(songList);
System.out.println(songSet);
如果想把两个不同的Song对象视为相等的,就必须覆盖过从Object继承下来的hashCode()方法与equals()方法。
由于内存问题,必须覆盖hashCode()才能确保两个对象有相同的hashcode,也要确保以另一个对象为参数的equals()调用会返回true。
重写hashCode()与equals()的代码:
public boolean equals(Object asong){
Song s=(Song)asong;
return getTitle().equals(s.getTitle());
}
public int hashCode(){
return title.hashCode();
}
//示例代码:
if(foo.equals(bar)&&foo.hashCode()==bar.hashCode()){
//两个引用指向同一个对象,或者两个对象是相等的。
}
hashCode()与equals()的相关规定:
API文件有对对象的状态制定出必须遵守的规则:
(1)如果连个对象相等,则hashcode必须也是相等的。
(2)如果两个对象相等,对其中一个对象调用equals()必须返回true。也就是说,若a.equals(b)则b.equals(a)。
(3)如果两个对象有相同的hashcode值,它们也不一定是相等的。但若两个对象相等,则hashcode值一定是相等的。
(4)因为若equals()被覆盖过,则hashCode()也必须被覆盖。
(5)hashCode()的默认行为是对在heap上的对象产生独特的值。如果你没有override过hashCode(),则该class的两个对象怎样都不会被认为是相同的。
(6)equals()的默认行为是执行==的比较。也就是说会去测试两个引用是否对上heap上同一个对象。如果equals()没有被覆盖过,两个对象永远不会被视为相同的,因为不同的对象有不同的字节组合。
a.equals(b) 必须与a.hashCode()==b.hashCode()等值。
但a.hashCode()==b.hashCode()不一定要与a.equals等值。
为什么不同对象会有相同的hashcode的可能?
因为hashCode()所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。
hashcode是用来缩小寻找成本,但最后还是要用equals()才能认定是否真的找到相同的项目。
关于泛型的笔记:TestGenerics1.java
import java.util.*;
public class TestGenerics1{
public static void main(String[] args){
new TestGenerics1().go();
}
public void go(){
/*Animal[] animals={new Dog(),new Cat(),new Dog()};
Dog[] dogs={new Dog(),new Dog(),new Dog()};
takenAnimals(animals);
takenAnimals(dogs); */
ArrayList<Animal> animals=new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
takeAnimals(animals);
ArrayList<Dog> dogs=new ArrayList<Dog>();
dogs.add(new Dog());
dogs.add(new Dog());
takeAnimals(dogs); //方法二时,这行代码无法通过编译
//将产生的错误如下(使用takeAnimals方法二):
/*
TestGenerics1.java:22: 无法将 TestGenerics1 中的 takeAnimals(java.util.ArrayList
<Animal>) 应用于 (java.util.ArrayList<Dog>)
takeAnimals(dogs);
^
1 错误
*/
/*
上面代码没问题,但是如下代码有就会有问题了。
public void takeAnimals(ArrayList<Animal> animals){
animals.add(new Cat())
}
如果把方法声明成取用ArrayList<Animal>,它就只会取用ArrayLis<Animal>参数,ArrayList<Dog>与ArrayList<Cat>都不行。
数组的类型是在运行期间检查的,但集合的类型检查只会发生在编译期间
takeAnimals(Animal[] animals)函数中写入animals[0]=new Cat();调用takeAnimals(dogs)编译时不会出错~
*/
}
//takeAnimals方法一
/*public void takenAnimals(Animal[] animals){
for(Animal a:animals){
a.eat();
}
}*/
//takeAnimals方法二
/*public void takeAnimals(ArrayList<Animal> animals){
for(Animal a:animals){
a.eat();
//animals.add(new Cat()); //这行代码无法通过编译
}
}*/
//takeAnimals方法三
public <T extends Animal>void takeAnimals(ArrayList<T> animals){
for(Animal a:animals){
a.eat();
//animals.add(new Cat()); //这行代码无法通过编译
}
}
/*
方法中使用万用字符,编译器阻止任何可能破坏引用参数所指集合的行为。
animals.add(new Cat());仍然无法通过编译
*/
}
//-----------------------------------------------------------------------------
abstract class Animal
{
void eat(){
System.out.println("animal eating");
}
}
class Dog extends Animal
{
void bark(){}
}
class Cat extends Animal
{
void meow(){}
}
/*
方法中使用完用字符的两种形式:
1.public void takeThing(ArrayList<? extends Animal> list)
2.public <T extends Animal> void takeThing(ArrayList<Tl> list)
如果方法中含有多个参数,这种形式将比较简便
*/
后记:觉得head first java某些东西还是讲的复杂了。比如关于takeAnimals(ArrayList<Animal> animals)方法的部分,只说只能传入Animal类的对象、而不是Animal子类的对象,就可以了啊。