head first java数据结构笔记(下).rtf

为什么要用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子类的对象,就可以了啊。




你可能感兴趣的:(java,数据结构,算法,Go)