18.1 集合与泛型

前言:Java集合框架能支持大多数数据结构,如创建自动排除重复项目的列表,可加入元素的列表


应用场景:点歌数据记录在一个文本文件


目标:管理点播记录,产生报表,管理歌本(依靠内存上的数据集合和记录用的文本文件)


前面已经叙述如何读取与解析文件,并用ArrayList排序


import java.util.*;

import java.io.*;


public class Jukebox1 {


  ArrayList<String> songList = new ArrayList<String>();

  public static void main(String[] args){

     new Jukebox1().go();

  }

 

   public void go(){  //载入文件并列出内容

     getSongs();

     System.out.printIn(songList);

  }

 

  void getSongs(){

    try{

     File file = new File("SongList.txt");

     BufferedReader reader = new BufferedReader(new FileReader(file));

     String line = null;

     while((line = reader.readLine()) != null){

      addSong(line);

     }

  }catch(Exception ex){

    ex.printStackTrace();

  }

}


   void addSong(String lineToParse){

    String [] tokens = LineToParse.split("/") //用反斜线拆开歌曲内容

    songList.add(tokens[0]);  //只添加歌名

   }

}


上例问题在于歌曲顺序不是按字母排序,查询ArrayList没有排序方法


ArrayList不是唯一的集合,还有其他集合,比如


TreeSet

以有序状态保持并可防止重复

HashMap

可用成对的name/value保存与取出

LinkedList

针对经常插入和删除元素所设计的集合

HashSet

防止重复的集合,可快速找寻相符的元素

LinkedHashMap

类似哈希图,但可记住元素插入顺序,也可设定为依照元素上次存取的先后来排序


综上,将字符串放入哈希图可确保按字母排序,但当增加新元素时,会增加排序的成本(ArrayList直接放在后面就行了


Collections类有个sort()方法,参数为list,而ArrayList实现了list接口,所以根据多态机制,可以把ArrayList传入该方法,则修改为:


public void go(){  //载入文件并列出内容

     getSongs();

     System.out.printIn(songList);

     Collections.sort(songList);//按字母排序

     System.out.printIn(songList);

  }


程序要求提升


list里面改为Song这个类的实例


toString()定义在Object类中,所以所有类都会继承到,因为对象被 System.out.printIn(object);列出时,都会调用toString(),所以当把list列出时,每个对象的toString()都会被调用一次,


class Song{

   String title

   String artist;

   String rating;

   String bpm;

  

   Song(String t,String a ,String r,String b){

    title=t;artist=a;rating=r;bpm=b;

   }

  publiv String getTitle(){

    return title;

  }

  public String getArtist(){

   return artist;

  }

 .....

  public String toString(){

   return title;//覆盖该方法,返回歌名

  }

}


修改

ArrayList<Song> songList = new ArrayList<Song>();


void addSong(String lineToParse){

    String [] tokens = LineToParse.split("/") //用反斜线拆开歌曲内容

    Song nextSong = new Song(tokens[0],tokens[1],tokens[2],tokens[3]);

    songList.add(nextSong);  //只添加歌名

   }


但编译型报错,表示没有sort()方法,问题出现了:ArrayList可应对string对象但应对不了song对象


重新看API文件,sort()的参数为List<T>,因为sort运用到泛型,<>表示泛型


泛型意味着更好的类型安全性


几乎所有以泛型写的程序都与处理集合有关,泛型主要目的是写出有类型安全性的集合即让编译器帮忙防止将dog加入cat集合中


泛型之前,所有集合都写成处理Object类型,可以把任何对象放入集合中


没有泛型


各种对象以引用形式加入到ArrayList中,没办法声明ArrayList的类型


取出来时会是Object的引用


使用泛型


仅有相应对象能以引用形式加入ArrayList中,创建类型安全性更好的集合


取出来是会是某一特定对象(如鱼对象)的引用


关于泛型


1)创建被泛型化类(如ArrayList)的实例 /创建泛型的类的实例


创建ArrayList时必须指定所容许的对象


new ArrayList<Song>()


2)声明与指定泛型类型的变量


多态遇到泛型类型


List<Song> songlist = new ArrayList<Song>()


3)声明与调用取用泛型类型的方法


若某方法取用Song对象ArrayList的参数


void foo(List<song> list)


使用泛型的类


ArrayList是最常用的泛型化类,ArrayList的文件如下


public class ArrayList <E> extends AbstractList<E> implements List<E>...{

   public boolean add(E o)

   //more

}

E代表ArrayList中元素类型(类型参数),ArrayList是AbstractList的子类,所以指定给ArrayListd的类型会自动用在AbstractList上,注意实现了list接口


泛型的类代表类的声明用到类型参数,泛型的方法代表方法声明,特征用到类型参数


方法中类型参数的不同运用方式


1)使用定义在类声明的类型参数


public class ArrayList<E> extends AbstractList <E>...{

     public boolean add(E o)//只能在此使用E,因为其被定义为类的一部分


当声明类的类型参数时,可以直接把类或接口类型用在任何地方,如add(E o),参数的类型声明会以用来初始化类的类型,即ArrayList<E>中的<E>来取代


2)使用未定义在类声明的类型参数


public <T extends Animal> void take(ArrayList<T> list)


前面已经声明T,此处ArrayList<T> list可以使用<T>


若类(ArrayList)本身未使用类型参数(<T extends Animal>),可以通过其他位置指定给方法(在返回类型之前,void之前)


对比


1)public <T extends Animal> void take(ArrayList<T> list)


2)public  void take(ArrayList<Animal> list)


两语句意义不同,<T extends Animal>是方法声明的一部分,表示任何声明为Animal或其子类的ArrayList是合法的,可使用其子类ArrayList<Dog>来调用方法


ArrayList<Animal> list代表只能此类型是合法的,子类不可以


回到sort()方法,对Song对象不能排序,String对象则可以,因为其只能接受Comparable对象或其子类的list,Song不符合条件,所以不行


public static <T extends Comparable<? super T>>void sort(List<T> list)


<? super T>表示Comparable的类型参数必须是T的或T的父型


List<T> list表示仅能传入继承Comparable的参数化类型的list


Comparable实际是个接口,String只是实现该接口,extends似乎不合理


以泛型的观点来说,extend代表extend或implement,适用于类和接口


Java有提供一种对参数化类型加上限制的方法,所以可加上限制,如只能是Dog的子类,但有时该限制是只允许有实现某特定接口的类,所以说需要出现皆可适用的语法,即

extend代表extend或implement


<T extends Comparable>理解为实现该接口的类型


所以Song类需要实现Comparable接口,即


public interface Comparable<T>{

   int compareTo(T o);

}


compareTo比较大小的方式:从某个Song对象来调用,然后传入其他Song对象的引用,进行判别谁先谁后,返回负值,代表传入方法的对象大于执行方法的对象,反之,相反,返回0,代表相等(只是该比较大小方式相等)。


更新,更好,更comparable的Song类


String本身有方法来比较字母的先后顺序


程序修改为:


class Song implements Comparable<Song>{

   String title

   String artist;

   String rating;

   String bpm;

  

  public int compareTo(Song s){//s为要传入比较的对象

     return title.compareTo(s.getTitle());//title为String类型,待比较的歌曲名

   Song(String t,String a ,String r,String b){

    title=t;artist=a;rating=r;bpm=b;

   }


集合的元素排序只能实现一个compareTo(),即sort()方法只能按一种方式排序


需要增加按歌星排列的情况


查询API,发现sort()方法可取用Comparator参数


sort()有重载版本,可以取用Comparator参数,还需要取得能比较歌星的Comparator


使用自制的Comparator


使用compareTo()方法时,sort()方法只能按一种方式排序,但Comparator是独立于所比较元素之外的,即是独立的类,可采用不同的比较方法,再调用参数为List和Comparator的sort()方法


取用参数为List和Comparator的sort()方法,则该方法以Comparator来排序,进一步地调用Comparator的compare()方法


public interface Comparator<T>{

   int compare(T o1,T o2);

}


总结:

1)调用单一参数的sort(List o)方法代表由list元素的compareTo()方法决定顺序,所以元素必须实现Comparable这个接口


2)调用sort(List o,Comparator c)方法,则该方法以Comparator来排序,进一步地调用Comparator的compare()方法,元素不需要实现Comparable这个接口



新的版本


(1)创建并实现Comparator的内部类,以compare()方法来取代compareTo()方法

(2)创建Comparator的实例

(3)调用重载版的sort(),传入歌曲的list和Comparator的实例


修改内容

  class ArtistCompare implements Comparator<Song>{

        public int compare(Song one,Song two){

          return one.getArtist().compareTo(two.getArtist());

        //String的compareTo()方法,one.getArtist()返回String

      }

 }


  ArtistCompare artistCompare = new ArtistCompare();

  Collections.sort(songList,artistCompare);






猜你喜欢

转载自blog.csdn.net/lwz45698752/article/details/79211284