1. 程式人生 > >set,map,list集合的特點及其排序的方法

set,map,list集合的特點及其排序的方法

特點:
list:儲存: 有序的 可重複的
訪問:可以for迴圈,foreach迴圈,iterator迭代器 迭代。
set:儲存:無序的 不重複的
訪問:可以foreach迴圈,iterator迭代器 迭代
map:儲存:儲存的是一對一對的對映 ”key=value“,key值 是無序,不重複的。value值可重複
訪問:可以map中key值轉為為set儲存,然後迭代這個set,用map.get(key)獲取value
也可以 轉換為entry物件 用迭代器迭代

Set集合排序

2017年10月03日 11:08:08閱讀數:4202

TreeSet使用元素的自然順序對元素進行排序,或者根據建立set時提供的Comparator進行排序,具體取決於使用的構造方法。通俗一點來說,就是可以按照排序後的列表顯示,也可以按照指定的規則排序。

Set<String> set = new TreeSet<String>();       
        set.add("f");  
        set.add("a");  
        set.add("b");  
        set.add("c");  
        set.add("d");  
        set.add("e");
System.out.println(set);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

輸出:[a, b, c, d, e, f] ,按照排序後輸出

若想它倒序輸出,可以指定一個規則讓他倒序輸出

public class TreeSetTest3 {  
     public static void main(String[] args) {  
        Set<String> set = new TreeSet<String>(new MyComparator());  
        set.add("a");  
        set.add("b"
); set.add("c"); set.add("d"); set.add("e"); set.add("A"); for(Iterator<String> iterator = set.iterator();iterator.hasNext();){ System.out.print(iterator.next()+" "); } } } class MyComparator implements Comparator<String>{ @Override public int compare(String o1, String o2) { return o2.compareTo(o1);//降序排列 } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

輸出:e d c b a A

如果Set集合中放入的是我們自己定義的一個類型別呢?

注意:一定要定義一個排序規則類實現Comparator介面,與上面的方法類似

public class TreeSetTest2 {  
    public static void main(String[] args) {  
        Set<Person> set = new TreeSet<Person>(new PersonComparator());  
        Person p1 =  new Person(10);  
        Person p2 =  new Person(20);  
        Person p3 =  new Person(30);  
        Person p4 =  new Person(40);  
        set.add(p1);  
        set.add(p2);  
        set.add(p3);  
        set.add(p4);         
        for(Iterator<Person> iterator = set.iterator();iterator.hasNext();){  
            System.out.print(iterator.next().score+" ");  
        }  
    }  
}  
class Person{  
    int score;  
    public Person(int score){  
        this.score = score;  
    }     
    public String toString(){  
        return String.valueOf(this.score);  
    }  
}  
class PersonComparator implements Comparator<Person>{  
    @Override  
    public int compare(Person o1, Person o2) {      
        return o1.score - o2.score;  
    }      
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

輸出:10 20 30 40

如果按照一個人的分數的倒序排列,只需要更改compare方法中的o2.score-o1.score


LinkedHashMap的特性:

LinkedHashMap的特性

LinkedHashMap的特性:

   Linked內部含有一個private transient Entry header;來記錄元素插入的順序或者是元素被訪問的順序。利用這個線性結構的物件,可以幫助記錄entry加入的前後順序或者記錄entry被訪問的頻率(最少被訪問的entry靠前,最近訪問的entry靠後)。大致的過程如下:

new LinkedHashMap(10, 0.75, true);
其中前面兩個引數就是HashMap建構函式需要的引數,後面的true表明LinkedHashMap按照訪問的次序來排序(即accessOrder為true)
按照訪問的次序來排序的含義:當呼叫LinkedHashMap的get(key)或者put(key, value)時,碰巧key在map中被包含,那麼LinkedHashMap會將key物件的entry放線上性結構的最後。
按照插入順序來排序的含義:呼叫get(key), 或者put(key, value)並不會對線性結構產生任何的影響

正是因為LinkedHashMap提供按照訪問的次序來排序的功能,所以它才需要改寫HashMap的get(key)方法(HashMap不需要排序)和HashMap.Entry的recordAccess(HashMap)方法
public Object get(Object key) {
        Entry e = (Entry)getEntry(key);
        if (e == null)
            return null;
e.recordAccess(this);
        return e.value;
    }

void recordAccess(HashMap m) {
            LinkedHashMap lm = (LinkedHashMap)m;
            if (lm.accessOrder) {
                lm.modCount++;
remove();
                addBefore(lm.header);
            }
        }
注意addBefore(lm.header)是將該entry放在header線性表的最後。(參考LinkedHashMap.Entry extends HashMap.Entry 比起HashMap.Entry多了before, after兩個域,是雙向的)

至於put(key, value)方法, LinkedHashMap不需要去改寫,用HashMap的就可以了,因為HashMap在其put(key, value)方法裡邊已經預留了e.recordAccess(this);

還有一個方法值得關注:
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return false;
    }
當呼叫put(key, value)的時候,HashMap判斷是否要自動增加map的size的作法是判斷是否超過threshold, LinkedHashMap則進行了擴充套件,如果removeEldestEntry方法return false;(預設的實現),那麼LinkedHashMap跟HashMap處理擴容的方式一致;如果removeEldestEntry返回true,那麼LinkedHashMap會自動刪掉最不常用的那個entry(也就是header線性表最前面的那個)。

這會造成嚴重的效能問題嗎?答案當然是否定的。因為在這兒的連結串列操作是常量級的。這也是LinkedHashMap/Set在這兒比TreeMap/Set效能更高的原因。

同樣,LinkedHashMap/Set也不是thread-safe的。如果在多執行緒下訪問,是需要進行外部同步,或者使用Collections.synchronizedMap()的方法包裝成一個thread-safe的Map/Set。

特別需要注意的是,在使用“訪問順序”時,讀取節點操作也是“結構變化”的操作。因為,這會改變元素遍歷的順序。所以,在使用LinkedHashMap的iterator()方法,遍歷元素時,如果其它執行緒有讀取操作,也要進行同步。否則,也會丟擲同其它fail-fast一樣的由於刪除或增加操作而引起的CurrentModificationException的例外。
最後,LinkedHashMap預設是使用插入順序的,如何構造一個訪問順序的LinkedHashMap呢?很簡單:public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) accessOrder = true 即可。


/**

 * 電子墨水屏安卓端查詢學生課程按照排列順

序將資料儲存到list中

 */
@Override
public List<String> couList(List<UserCourseEntity> tUserCours) throws ParseException {
List<String> list = new ArrayList<String>();
Set<String> timeSet = new LinkedHashSet<String>();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
//遍歷集合將上課時間與第幾節課放進set中,定義Set<String> timeSet = new LinkedHashSet<String>();可以將set中的資料按照新增順序存放
for (UserCourseEntity userCourseEntity : tUserCours) {
String startTime = sdf.format(sdf.parse(userCourseEntity.getStartTime()));
String endTime = sdf.format(sdf.parse(userCourseEntity.getEndTime()));
String periodNum = userCourseEntity.getPeriodNum();
String time = startTime+"~"+endTime;
boolean res = timeSet.add(time+"-"+periodNum);
if (!res) {
continue;
}
}
//遍歷set集合與課程集合將第幾節課與對應課程鍵值對關聯定義LinkedHashMap<String,List<String>> map = new LinkedHashMap<String,List<String>>();也可以將資料按照新增順序存放
LinkedHashMap<String,List<String>> map = new LinkedHashMap<String,List<String>>();
for (String str : timeSet) {
for (UserCourseEntity course : tUserCours) {
String ts = sdf.format(sdf.parse(course.getStartTime()))+"~"+sdf.format(sdf.parse(course.getEndTime()))+"-"+course.getPeriodNum();
if(ts.equals(str)){
List<String> couList = map.get(ts);
if(couList==null){
couList = new ArrayList<String>();
}
couList.add(course.getCourseName());
map.put(ts, couList);
}
}
}
//遍歷map.keySet()與 map.get(key)分別按對應順序放入list中
for (String key : map.keySet()) {
String periodNum = key.split("-")[1];
String time = key.split("-")[0];

list.add(periodNum);
list.add(time);
for (String courseName : map.get(key)) {
list.add(courseName);
}
}
return list;
}

}

      今天做統計時需要對X軸的地區按照地區程式碼(areaCode)進行排序,由於在構建XMLData使用的map來進行資料統計的,所以在統計過程中就需要對map進行排序。

一、簡單介紹Map

       在講解Map排序之前,我們先來稍微瞭解下map。map是鍵值對的集合介面,它的實現類主要包括:HashMap,TreeMap,Hashtable以及LinkedHashMap等。其中這四者的區別如下(簡單介紹):

       HashMap:我們最常用的Map,它根據key的HashCode 值來儲存資料,根據key可以直接獲取它的Value,同時它具有很快的訪問速度。HashMap最多隻允許一條記錄的key值為Null(多條會覆蓋);允許多條記錄的Value為 Null。非同步的。

      TreeMap: 能夠把它儲存的記錄根據key排序,預設是按升序排序,也可以指定排序的比較器,當用Iterator 遍歷TreeMap時,得到的記錄是排過序的。TreeMap不允許key的值為null。非同步的。

      Hashtable: 與 HashMap類似,不同的是:key和value的值均不允許為null;它支援執行緒的同步,即任一時刻只有一個執行緒能寫Hashtable,因此也導致了Hashtale在寫入時會比較慢。

      LinkedHashMap: 儲存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄肯定是先插入的.在遍歷的時候會比HashMap慢。key和value均允許為空,非同步的。

二、Map排序

TreeMap

      TreeMap預設是升序的,如果我們需要改變排序方式,則需要使用比較器:Comparator。

      Comparator可以對集合物件或者陣列進行排序的比較器介面,實現該介面的public compare(T o1,To2)方法即可實現排序,該方法主要是根據第一個引數o1,小於、等於或者大於o2分別返回負整數、0或者正整數。如下:

複製程式碼
public class TreeMapTest {
    public static void main(String[] args) {
        Map<String, String> map = new TreeMap<String, String>(
                new Comparator<String>() {
                    public int compare(String obj1, String obj2) {
                        // 降序排序
                        return obj2.compareTo(obj1);
                    }
                });
        map.put("c", "ccccc");
        map.put("a", "aaaaa");
        map.put("b", "bbbbb");
        map.put("d", "ddddd");
        
        Set<String> keySet = map.keySet();
        Iterator<String> iter = keySet.iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            System.out.println(key + ":" + map.get(key));
        }
    }
}
複製程式碼

      執行結果如下:

      d:ddddd 
      c:ccccc 
      b:bbbbb 
      a:aaaaa

      上面例子是對根據TreeMap的key值來進行排序的,但是有時我們需要根據TreeMap的value來進行排序。對value排序我們就需要藉助於Collections的sort(List<T> list, Comparator<? super T> c)方法,該方法根據指定比較器產生的順序對指定列表進行排序。但是有一個前提條件,那就是所有的元素都必須能夠根據所提供的比較器來進行比較。如下:

複製程式碼
public class TreeMapTest {
    public static void main(String[] args) {
        Map<String, String> map = new TreeMap<String, String>();
        map.put("d", "ddddd");
        map.put("b", "bbbbb");
        map.put("a", "aaaaa");
        map.put("c", "ccccc");
        
        //這裡將map.entrySet()轉換成list
        List<Map.Entry<String,String>> list = new ArrayList<Map.Entry<String,String>>(map.entrySet());
        //然後通過比較器來實現排序
        Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
            //升序排序
            public int compare(Entry<String, String> o1,
                    Entry<String, String> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
            
        });
        
        for(Map.Entry<String,String> mapping:list){ 
               System.out.println(mapping.getKey()+":"+mapping.getValue()); 
          } 
    }
}
複製程式碼

      執行結果

      a:aaaaa 
      b:bbbbb 
      c:ccccc 
      d:ddddd

HashMap

      我們都是HashMap的值是沒有順序的,他是按照key的HashCode來實現的。對於這個無序的HashMap我們要怎麼來實現排序呢?參照TreeMap的value排序,我們一樣的也可以實現HashMap的排序。

複製程式碼
public class HashMapTest {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("c", "ccccc");
        map.put("a", "aaaaa");
        map.put("b", "bbbbb");
        map.put("d", "ddddd");
        
        List<Map.Entry<String,String>> list = new ArrayList<Map.Entry<String,String>>(map.entrySet());
        Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
            //升序排序
            public int compare(Entry<String, String> o1,
                    Entry<String, String> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
            
        });
        
        for(Map.Entry<String,String> mapping:list){ 
               System.out.println(mapping.getKey()+":"+mapping.getValue()); 
          } 
     }
}
複製程式碼

      執行結果

      a:aaaaa 
      b:bbbbb 
      c:ccccc 
      d:ddddd