排序在java中只是雕虫小技。java的集合框架( Collection Framework )能够支持大多数你会用到的数据结构。它可以有 容易加入元素的列表、根据名称来搜索、自动排除重复项目的列表……
事件起因:我们假设有一个song类,它有四个实例title、artist、rate、bgm,此时我们想对它进行排序工作!我们把这些song方便的放入ArrayList中,但是它却没有sort方法。
解决方案:使用TreeSet或者Collections.sort()方法。
TreeSet:如果把字符串放进TreeSet而不是ArrayList,这些String会自动地按照字幕的顺序排在正确的位置。没当你想要列出清单时,元素总是会以字母顺序出现。(稍后讨论)
Collection.sort():以下为方法详解
http://www.cjsdn.net/Doc/JDK60/java/util/Collections.html#sort(java.util.List)
作为Collections的静态方法,它可以对List进行排序,而感谢多态机制,ArrayList可以传入此方法。注意此处我们传入的是String[](暂时先没有用song类),根据sort方法中的参数类型<T extends Comparable<? super T>>,它是符合的,为什么呢?
*******************************************************插入************************************************************
泛型意味着更好的类型安全性,几乎所有你会以泛型写的程序都会与处理集合有关。它主要目的还是让你能够写出有类型安全性的集合。也就是让编译器帮忙防止你把Dog加到一群Cat中。如果没有泛型,编译器无法注意到你加入集合中的东西是什么,因为所有的集合都写成处理Object类型。有点像是ArrayList<Object>。
关于泛型:
1、创建被泛型化类的实例。
下面这行程序
ArrayList<String> thislist = new ArrayList<String>代表这个ArrayList:(参照ArrayList的说明文件http://www.cjsdn.net/Doc/JDK60/java/util/ArrayList.html)
public class ArrayList<E> extends AbstractList<E>...{ public boolean add(E o) //更多代码 }会被编译器这样看待:
public class ArrayList<String> extends AbstractList<String>...{ public boolean add(String o) //更多代码 }也就是说,E会被所指定的真正类型所取代(又被称为类型参数)。
public <T extends Animal> void takeThing(ArrayList<T> list)跟下面这个是不一样的
public void takething(ArrayList<Animal> list)
第一个的方法表示被声明为Animal或者Animal的子类的ArrayList都是合法的。因此你可以使用ArrayList<Dog>、ArrayList<Cat>或者ArrayList<Animal>来调用上面的方法。
而第二个方法代表只有ArrayList<Animal>是合法的。
*******************************************************插入结束*************************************************
再看一下sort方法中<T extends Comparable<? super T>>,然而String只实现了Comparable<String>接口,所以此处的extends意为extend或者implement,表示的意思是“是一个”,且不管接口或类都适用。
我们再看一下Comparable接口,它只有一个方法
http://www.cjsdn.net/Doc/JDK60/java/lang/Comparable.html
也就是说,除了java给我们已经implement该接口的类,我们自己的类只要implement接口并实现方法,就可以sort了。假设我有个song类(title,artist,rating,bpm四个实例),我想按照title顺序排序,我只需要
class song implement Comparable<Song>{ public int comparaTo(Song s){ return title.compareTo(s.getTitle()); } }即可用sort方法对ArrayList(Song)进行排序。
那如果又有新的要求了(天啊),需要用artist进行排序,而我们的compareTo只能实现一个啊!怎么办?则用sort的重载办法更加合理
而这个Comparator又是什么?http://www.cjsdn.net/Doc/JDK60/java/util/Comparator.html
所以,如果传Comparator给sort()方法,则排序是由Comparator而不是元素的comparaTo()方法来决定。所以需要以下几步就可以实现更新操作:
(1)创建并实现Comparator的内部类,以compare()方法取代compareTo()方法。
(2)制作该类的实例。
(3)调用重载版的sort(),传入歌曲的list以及Comparator的实例。
关键操作如下:
...... class ArtistCompare implements Comparator<Song>{ public int compare(Song one,Song two){}{ return one.getArtist().compareTo(two.getArtist()); } } ...... ArtistCompare artistCompare = new ArtistCompare(); Collections.sort(songList,artistCompare);
这样就可以很方便的进行更新操作。
出现问题:数据有重复
解决方案:我们来看看Collection还有哪些宝藏
(1)List:对付顺序的好帮手
是一种知道索引位置的集合
List知道某物在系列集合中的位置。可以有多个元素引用相同的对象。
(2)SET:注重独一无二的性质
不允许重复的集合
它知道某物是否已经存在于集合中。不会有多个元素引用相同的对象。
(3)MAP:用key来搜索的专家
使用成对的键值和数据值。
Map会维护与key有关联的值。两个key可以引用相同的对象,但key不能重复,典型的key会是String,但也可以是任何对象。
尝试1:采用HashSet取代ArrayList
如果直接这样做
HashSet<Song> songSet = new HashSet<Song>(); songSet.addAll(SongList);//SongList为ArrayList则此时打印出的结果不但没有去重,还被打乱了顺序。所以 对象要怎样才算相等?这带出两个议题
引用相等性:堆上同一对象的两个引用相等(hashCode()方法返回相同(返回的是每个对象特有的序号)且引用的字节数也会一样(==得出相同))。
对象相等性:堆上的两个对象在意义上是相等的。(必须通过覆盖hashCode()方法以及equals()方法才可以)
所以HashSet检查重复是靠hashCode()以及equals()。
穿插问题:为什么有hashCode()还要equals()?
解答:因为杂凑算法会存在碰撞的情况,两个不同的对象会传回相同的杂凑值。也就是说,hashCode是用来缩小寻找成本,但最后还是要用equals()才能认定是否真的找到相同的项目。
回归问题,我们在song中重写这两个方法就哦啦!
public boolean equals(Object aSong){ Song s = (Song) aSong; return getTitle().equals(s.getTitle());//因为歌名是String,且String本来就覆盖过的equals().所以我们可以调用 } public int hashCode(){ return title.hashCode();//String也覆盖过hashCode.注意hashCode()与equals()使用相同的实例变量 }
尝试方法2:用TreeSet
TreeSet<Song> songSet = new TreeSet<Song>();//调用没有餐宿的构造函数来用TreeSet取代HashSet意味着以对象的compareTo()方法来进行排序 songSet.addAll(SongList);当然也可以用comparator去排序,写好comparator塞入它就好啦!和前面sor方法类似,就不写了。
***********************************************分割线**********************************************************************************************************************************
问题结束,再来看一下Map。
Map中的元素实际上是两个对象:关键字和值。值可以重复,但是key不行。一个例子就懂基本了:
import java.util.*; public class newTest{ public static void main(String[] args){ HashMap<String,Integer> scores = new HashMap<String,Integer>(); scores.put("Kathy",42); scores.put("Bert",343); scores.put("Skyler",420); System.out.println(scores); System.out.println(scores.get("Bert")); } }
结果输出正确。
终于回到泛型,以多态化的观点来看之前的泛型,发现有一些古怪的,接下来我们再深入一些验证一下。
先定义三个类,它们有继承关系
abstract public class Animal { void eat() { System.out.println("animal eat"); } }
public class Cat extends Animal { void meow() { } }
public class Dog extends Animal { void bark() { } }使用多态参数与泛型
import java.util.ArrayList; import java.util.List; public class TestGenericsl { public static void main(String[] orgs){ new TestGenericsl().go(); } public void go(){ ArrayList<Animal> animals = new ArrayList<Animal>(); animals.add(new Dog()); animals.add(new Cat()); animals.add(new Dog()); takeAnimals(animals); } public void takeAnimals(ArrayList<Animal> animals){ for(Animal a:animals){ a.eat(); } } }输出结果没有问题
这和我们之前用的Animal[ ]数组(稍微想象一下)的工作方式差不多。
幺蛾子来啦!此时修改一下片段:
// ArrayList<Animal> animals = new ArrayList<Animal>(); ArrayList<Dog> dogs = new ArrayList<Dog>(); // animals.add(new Dog()); dogs.add(new Dog()); // animals.add(new Cat()); dogs.add(new Dog()); // animals.add(new Dog()); // takeAnimals(animals); takeAnimals(dogs);此时编译器会告诉你
The method takeAnimals(ArrayList<Animal>) in the type TestGenericsl is not applicable for the arguments (ArrayList<Dog>)
多态的意义在于Animal可以做的事情,Dog也都能做。所以对Dog引用调用eat()应该没问题啊。但是如果该方法里如下:
public void takeAnimals(ArrayList<Animal> animals){ for(Animal a:animals){ animals.add(new Cat()); a.eat(); } }那就会出问题了,Dog中会混进一个Cat。所以如果把声明成取用ArrayList<Animal>,它就只会去用ArrayList<Animal>参数,ArrayList<Dog>与ArrayList<Cat>都不行。
而同样的问题,数组会在运行时被java虚拟机发现。因为数组的类型是在运行期间检查的,但集合的类型检查只会发生在编译期间。那怎么多态化集合参数呢?
解决办法:采用万用字符
public void takeAnimals(ArrayList<? extends Animal> animals){ for(Animal a:animals){ a.eat(); } }这样就可以传入小猫小狗了,但是注意:在该方法中, 编译器不允许加入任何东西到集合众!
还有另一种方法声明,效果一样,根据情况自己选用:
public <T extends Animal> void takeThing(ArrayList<T> list)