java的集合

我們在前面學過Java數組,Java數組的長度是固定的,在同一個數組中只能存放相同的類型的數據。數組可以存放基本類型的數據,了可以存入對象引用的數據。

在創建數組時,必須明確指定數組的長度,數組一旦創建,其長度就不能被改變,在許多應用的場合,一組數據的數目是不固定的,比如一個單位的員工數目是變化的,有老的員工跳槽,也有新的員工進來。

爲了使程序能方便地存儲和操縱數目不固定的一組數據,JDK中提供了Java集合,所有Java集合類都位於java.util包中,與Java數組不同,Java集合不能存放基本數據類型數據,而只能存放對象的引用。

 

java集合類分爲三種:

Set(集合):集合中對象不按特定的方式排序。並且沒有重複對象,但它的有些實現類對集合中的對象按特定方式排序。

List(列表):集合中的對象按照索引位置排序,可以有重複對象,允許按照對象在集合中的索引位置檢索對象,List和數組有些相似。

Map(映射):集合中的每一個元素包含一對鍵對象和值對象,集合中沒有重複的鍵對象,值對象可以重複,它的有些實現類能對集合中的鍵對象進行排序。

 

Java的主要集合類的框架圖:

 

 Collection和Iterator接口:

Collection接口中聲明瞭適用於Java集合(只包括SetList)通用方法。

Collection接口的方法

方法

描述

boolean add(Object o)

向集合中加入一個對象的引用

void clear()

刪除中集合中所有對象,即不再對持有對象的引用

boolean contains(Object o)

判斷在集合中是否含有特定對象的引用

boolean isEmpty()

判斷集合是否爲空

Iterator iterator()

返回一個Iterator對象,可用它來遍歷集合中的元素

boolean remove(Object o)

從集合中刪除一個對象的引用

int size()

返回集合中元素的數目

Object[] toArray()

返回一個數組,該數組包含集合中的所有元素

 

 

Set接口和List接口都繼承了Collection接口,Map接口有繼承Collection接口,而繼承了因此可以對Set對象和List對象調用以上方法,但是不能對Map對象調用以上方法

Collection接口的iterator()toArray()方法都用於獲得集合中的所有元素,前者返回一個Iterator對象,後者返回一個包含集合中所有元素的數組。

Iterator隱藏底層集合的數據結構,向客戶程序提供了遍歷各種類型的集合的統一接口中。Iterator接口中聲明瞭如下方法:

l  hasNext():判斷集合中的元素是否遍歷完畢,如果沒有,就返回true.

l  next():返回下一個元素。

l  remove():從集合中刪除上一個由next()方法返回的元素

注意:如果集合中的元素沒有排序,Iterator遍歷集合中元素的順序是任意的,並不一定與向集合中加入元素的順序一致的

Set(集)

Set是最簡單一種集合,集合中的對象不按特定方式排序,並且沒有重複對象。Set接口主要有兩個實現類:HashSetTreeSetHashSet類按照哈希算法來存取集合中的對象,存取速度比較快。HashSet類還有一個子類LinkedHashSet類,它不僅實現了哈希算法,而且實現了鏈表數據結構,鏈表數據結構能提高插入和刪除元素的性能。TreeSet類實現了SortedSet接口中,具有排序功能。

List(列表)

List的主要特徵是其元素以線性方式存儲,集合中允許存放重複對象。List接口主要的實現類包括:

l  ArrayListArrayList代表長度可變的數組。允許對元素進行快速的隨機訪問,但是向ArrayList中插入與刪除元素的速度較慢。

 

l  LinkedList-在實現中採用鏈表數據結構。對順序訪問進行了優化,向List中插入和刪除元素的速度較快,隨機訪問速度則相對較慢,隨機訪問是指檢索位於特定索引位置元素。

Map(映射)

Map(映射)是一種把鍵對象和值對象進行映射的集合。它的每一個元素都包含一對鍵對象和值對象,而值對象仍可以是Map類型。依此類推,這樣就形成了多級映射。向Map集合中加入元素時,必須提供一對鍵對象和值對象,從Map集合上檢索元素只要給出鍵對象,就會返回對應的值對象。

 

實例1

CollectionAll.java

package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

public class CollectionAll {
  public static void main(String[] args) {
  	
    List list1 = new LinkedList();
    list1.add("我");
    list1.add("是");
    list1.add("誰");
    list1.add("我");
    traverse(list1);
     
    List list2 = new ArrayList();
    list2.add("我");
    list2.add("是");
    list2.add("誰");
    list2.add("我");
    traverse(list2); 
    
    Set set1 = new HashSet();
    set1.add("我");
    set1.add("是");
    set1.add("誰");
    set1.add("我");
    traverse(set1); 
    
    SortedSet set2 = new TreeSet();
    set2.add("我");
    set2.add("是");
    set2.add("誰");
    set2.add("我");
    traverse(set2); 
    
    LinkedHashSet set3 = new LinkedHashSet();
    set3.add("我");
    set3.add("是");
    set3.add("誰");
    set3.add("我");
    traverse(set3); 
    
    Map m1 = new HashMap();
    m1.put("name01", "我");
    m1.put("name02", "是");
    m1.put("name03", "誰");
    m1.put("name04", null);
    traverse(m1.keySet()); 
    traverse(m1.values()); 
     
    SortedMap m2 = new TreeMap();
    m2.put("name01", "我");
    m2.put("name02", "是");
    m2.put("name03", "誰");
    m2.put("name04", "我");
    traverse(m2.keySet()); 
    traverse(m2.values()); 
    
    LinkedHashMap m3 = new LinkedHashMap();
    m3.put("name01", "我");
    m3.put("name02", "是");
    m3.put("name03", "誰");
    m3.put("name04", "我");
    traverse(m3.keySet()); 
    traverse(m3.values()); 
    
    Hashtable numbers = new Hashtable();
    numbers.put("name01", "我");
    numbers.put("name02", "是");
    numbers.put("name03", "誰");
    numbers.put("name04", "我");
    traverse(numbers.keySet()); 
    traverse(numbers.values()); 
  }
  static void traverse(Collection coll) {
    Iterator iter = coll.iterator();
    while (iter.hasNext()) {
      String elem = (String) iter.next();
      System.out.print(elem + " ");
    }
    System.out.println();
  }
}


一、集合框架中的各種實現類

 

HashSet

HashSet類按照哈希算法一存取集中的對象,具有很好的存取和查找性能。當向集合中加入一個對象時,HashSet會調用對象的hashCode()方法來獲得哈希碼,然後根據這個哈希碼進一步計算出對象在集合中的存放位置。

 

實例2   MyHashSet.java

package collection;
import java.util.*;

 public class MyHashSet{
  public static  void main(String[] args) 
  {
    HashSet set = new HashSet(6);
    Object[] values={"Tom","Mike","Jack","Mary","Linda","Jone"};
    for(int i=0;i<values.length;i++)
      set.add(values[i]);  //向集合中加入對象
    set.remove("Mike");  //從集合中刪除一個對象
    System.out.println("size="+set.size());
    Iterator iter=set.iterator();  //獲得集合中的所有對象
    while (iter.hasNext()) {
      String elem = (String) iter.next();
      System.out.println(elem + " ");;
    }
    System.out.println(set.contains("Jack"));  //打印true
    System.out.println(set.contains("Linda")); //打印true
    System.out.println(set.contains("Mike"));  //打印false
  }
  }


 

TreeSet

TreeSet類實現了SortedSet接口,能夠對集合中的對象進行排序,以下程序創建了一個TreeSet對象,然後向集合中加入4Integer對象

 

實例3

TreeSetTest.java

package collection;
import java.util.*;

public class TreeSetTest {
	public static void main(String[] args) {   
        Set set=new TreeSet();
        set.add(new Integer(8));
        set.add(new Integer(7));
        set.add(new Integer(6));
        set.add(new Integer(9));
        Iterator it=set.iterator();
        while(it.hasNext()){
        	System.out.println(it.next()+"");
        }
     }
}


 

由此我們知道當TreeSet集合中加入一個對象時,會把它插入到有序的對象序列中。

 

ArrayList

ArrayListArrayList代表長度可變的數組。允許對元素進行快速的隨機訪問,它允許所有元素,包括nullArrayList線程不同步的,向ArrayList中插入與刪除元素的速度較慢

 

LinkedList

LinkedList-實現了List接口,允許null元素,在實現中採用鏈表數據結構。對順序訪問進行了優化,向List插入和刪除元素的速度較快,隨機訪問速度則相對較慢,隨機訪問是指檢索位於特定索引位置元素。

注意LinkedList沒有同步方法。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List

    List list = Collections.synchronizedList(new LinkedList(...));

 

 實例4

 StackL.java

package collection;
import java.util.LinkedList;

public class StackL {
  private LinkedList list = new LinkedList();
  public void push(Object v) {
    list.addFirst(v);
  }
  public Object top() {
    return list.getFirst();
  }
  public Object pop() {
    return list.removeFirst();
  }
  public static void main(String[] args) {
    StackL stack = new StackL();
    for (int i = 0; i < 10; i++)
      stack.push(new Integer(i));
    System.out.println(stack.top());
    System.out.println(stack.top());
    System.out.println(stack.pop());
    System.out.println(stack.pop());
    System.out.println(stack.pop());
  }
}


 

ListIterator接口

ListlistIterator()方法返回一個ListIterator對象,ListIterator接口繼承了Iterator接口中,此外還提供了專門操縱列表的方法。

l  add():向列表插入一個元素

l  hasNext():  判斷列表中是否還有下一個元素

l  hasPrevious(): 判斷列表中是否還有上一個元素

l  next(): 返回列表中的下一個元素

l  previous(): 返回列表中的上一個元素

實例5

ListInserter.java

import java.util.*;

public class ListInserter {
	
  /**  向List列表中按順序插入一數據 */
  public static void insert(List list,int data){
    ListIterator it=list.listIterator();
    while(it.hasNext()){
      Integer in=(Integer)it.next();
      if(data<=in.intValue()){
        it.previous();
        it.add(new Integer(data));  //插入元素
        break;
      }
    }
  }
  public static void main(String args[]){
    List list=new LinkedList();  //創建一個鏈接列表
    list.add(new Integer(3));
    list.add(new Integer(2));
    list.add(new Integer(5));
    list.add(new Integer(9));

    Collections.sort(list);//爲列表排序
    insert(list,6);  //向列表中插入一個元素
    
    ListIterator it=list.listIterator();
    while(it.hasNext()){
     Integer elem = (Integer) it.next();
      System.out.println(elem + " ");
    }
  }
}


 

實例6 比較Java數組和各種List的性能

  PerformanceTester..java

package collection;
import java.util.*;
public class PerformanceTester{
  private static final int TIMES=100000;

  public static abstract class Tester{
    private String operation;
    public Tester(String operation){this.operation=operation;}
    public abstract void test(List list);
    public String getOperation(){return operation;}
  }

  static Tester iterateTester=new Tester("iterate"){
      public void test(List list){  //迭代操作
       for(int i=0;i<10;i++){
          Iterator it=list.iterator();
          while(it.hasNext()){
            it.next();
          }
        }
      }
  };

  static Tester getTester=new Tester("get"){
      public void test(List list){  //隨機訪問操作
         for(int i=0;i<list.size();i++)
           for(int j=0;j<10;j++)
              list.get(j);
      }
  };

  static Tester insertTester=new Tester("insert"){
    public void test(List list){  //插入操作
      ListIterator it=list.listIterator(list.size()/2);  //從列表的中間開始
      for(int i=0;i<TIMES/2;i++)
        it.add("hello");
    }
  };
  static Tester removeTester=new Tester("remove"){  //執行刪除操作的匿名類
    public void test(List list){   //刪除操作
      ListIterator it=list.listIterator();
      while(it.hasNext()){
        it.next();
        it.remove();
      }
    }
  };

  static public void testJavaArray(List list){
     Tester[] testers={iterateTester,getTester};
     test(testers,list);
  }
  static public void testList(List list){
    Tester[] testers={insertTester,iterateTester,getTester,removeTester};
    test(testers,list);
  }
  static public void test(Tester[] testers,List list){
    for(int i=0;i<testers.length;i++){
      System.out.print(testers[i].getOperation()+"操作:");
      long t1=System.currentTimeMillis();
      testers[i].test(list);
      long t2=System.currentTimeMillis();
      System.out.print(t2-t1+" ms");
      System.out.println();
    }
  }
  public static void main(String args[]){
    List list=null;

    //測試Java數組
    System.out.println("----測試Java數組----");
    String[] ss=new String[TIMES];
    Arrays.fill(ss,"hello");
    list=Arrays.asList(ss);
    testJavaArray(list);

    ss=new String[TIMES/2];
    Collection col=Arrays.asList(ss);

    //測試Vector
    System.out.println("----測試Vector----");
    list=new Vector();
    list.addAll(col);
    testList(list);

    //測試LinkedList
    System.out.println("----測試LinkedList----");
    list=new LinkedList();
    list.addAll(col); 
    testList(list);

    //測試ArrayList
    System.out.println("----測試ArrayList----");
    list=new ArrayList();
    list.addAll(col);
    testList(list);
  }
}

從結果可以看出,對Java數組進行隨機訪問和迭代操作具有最快的速度;LinkedList進行插入和刪除具有最快的速度;對ArrayList進行隨機訪問也具有較快的速度,Vector類在各方面都沒有突出的性能,屬於歷史集合類,已經不提倡使用它。

Map

Map(映射)是一種把鍵對象和值對象進行映射的集合。它的每一個元素都包含一對鍵對象和值對象,而值對象仍可以是Map類型。依此類推,這樣就形成了多級映射。向Map集合中加入元素時,必須提供一對鍵對象和值對象,從Map集合上檢索元素只要給出鍵對象,就會返回對應的值對象。

 

HashMap

HashMap類按照哈希算法一存取Map中的對象,允許存儲空對象,而且允許鍵是空

實例7

Statistics.java

package collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

 class Counter {
  int i = 1;

  public String toString() {
    return Integer.toString(i);
  }
}
public class Statistics {
  private static Random rand = new Random();

  public static void main(String[] args) {
    Map hm = new HashMap();
    for (int i = 0; i < 10000; i++) {     // Produce a number between 0 and 20:
      Integer r = new Integer(rand.nextInt(20));
      if (hm.containsKey(r))
        ((Counter) hm.get(r)).i++;
      else
        hm.put(r, new Counter());
    }
    System.out.println(hm);
  }
}

Hashtable

Hashtable繼承Map接口,實現一個key-value映射的哈希表。任何非空(non-null的對象都可作爲key或者value

實例8

HashTable.java

package collection;

import java.util.Enumeration;
import java.util.Hashtable;

public class HashTable {
  public static void main(String args[]) {
    String names[] = { "張三", "李四", "王五", "趙六", "陳七",
        "孫九"};
    float diameters[] = { 4800f, 12103.6f, 12756.3f, 6794f, 142984f,
        120536f };
    Hashtable hash = new Hashtable();
    for (int i = 0, n = names.length; i < n; i++) {
      hash.put(names[i], new Float(diameters[i]));
    }
    Enumeration e = hash.keys();
    Object obj;
    while (e.hasMoreElements()) {
      obj = e.nextElement();
      System.out.println(obj + ": " + hash.get(obj));
    }
  }
}


 

HashMapHashTable的區別

 

Hashtable繼承自Dictionary類,而HashMapMap interface的一個實現

HashMap允許將null作爲一個的key或者value,而Hashtable不允許

還有就是,HashMapHashtablecontains方法去掉了,改成containsvaluecontainsKey。因爲contains方法容易讓人引起誤解。

最大的不同是Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己爲它的方法實現同步,而HashMap就必須爲之提供外同步

 

TreeMap

實例9

SortTreeMap.java

package collection;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

public class SortTreeMap {
  public static void main(String args[]) {
    String names[] = { "B", "E", "A", "M", "J",
        "C", "H", "D", "G" };
    float diameters[] = { 4800f, 12103.6f, 12756.3f, 6794f, 142984f,
        120536f, 51118f, 49532f, 2274f };
    Map map = new TreeMap();
    for (int i = 0, n = names.length; i < n; i++) {
      map.put(names[i], new Float(diameters[i]));
    }
    Iterator it = map.keySet().iterator();
    Object obj;
    while (it.hasNext()) {
      obj = it.next();
      System.out.println(obj + ": " + map.get(obj));
    }
  }
}


二、集合實用類: Collections

Java集合中,有一個實用類,即java.util.Collections它的一部分方法專門用於操縱List類型集合,還有一部分方法可用於操縱所有的Collection類型或Map類型的集合。

List代表長度可變的數組,Collections的以下方法適用於List類型的集合。

以下方法適用於Collection類型或Map類型的集合

實例10

collection.java

package collection;
import java.util.*;

public class collection {
	public static void main(String[] args) {   
         ArrayList arrayList1=new ArrayList();
	     arrayList1.add(new Integer(1));
	     arrayList1.add(new Integer(9));
	     arrayList1.add(new Integer(3));
	     arrayList1.add(new Integer(7));
	     arrayList1.add(new Integer(8));
	     arrayList1.add(new Integer(2));
	     arrayList1.add(new Integer(5));
	     arrayList1.add(new Integer(4));
	     arrayList1.add(new Integer(6));
	     System.out.print("目前ArrayList1數據內容如下:");
	     System.out.println(arrayList1);
	     Collections.sort(arrayList1);
	     System.out.println("排序後的內容如下:"+arrayList1);
	     Collections.reverse(arrayList1);
	     System.out.println("反排序後的內容如下:"+arrayList1);
	     Collections.shuffle(arrayList1);
	     System.out.println("重新洗牌後數據內容如下:"+arrayList1);
	     System.out.println("最大值"+Collections.max(arrayList1));
	     System.out.println("最小值"+Collections.min(arrayList1));
	     
	      
     }
}


三、集合類中排序

JDK類庫中,有一部分類實現了Comparable接口中,如IntegerDoubleString等。Comparable接口有一個compareTo(Object o)方法,它返回整數類型。對於表達式x.compareTo(y),如果返回值爲0,則表示xy相等地,如果返回值大於0,則表示x大於y,如果返回值小於0,則表示x小於y

TreeSet調用對象compareTo()方法比較集合中對象的大小

實例11

ComparatorTest.java

package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

 class Person implements Comparable {
  String firstName, lastName;

  public Person(String f, String l) {
    this.firstName = f;
    this.lastName = l;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String toString() {
    return "[ name=" + firstName + ",name=" + lastName + "]";
  }

  public int compareTo(Object obj) {
    Person emp = (Person) obj;
    int deptComp = firstName.compareTo(emp.getFirstName());

return ((deptComp == 0) ? lastName.compareTo(emp.getLastName())
        : deptComp);
  }

  public boolean equals(Object obj) {
    if (!(obj instanceof Person)) {
      return false;
    }
    Person emp = (Person) obj;
    return firstName.equals(emp.getFirstName())
        && lastName.equals(emp.getLastName());
  }
}
public class ComparatorTest implements Comparator {
  public int compare(Object obj1, Object obj2) {
    Person emp1 = (Person) obj1;
    Person emp2 = (Person) obj2;

    int nameComp = emp1.getFirstName().compareTo(emp2.getFirstName());

    return ((nameComp == 0) ? emp1.getLastName().compareTo(
        emp2.getLastName()) : nameComp);
  }

  public static void main(String args[]) {
    String names[] = { "張三", "李四", "王五", "趙六", "陳七", "孫八",
        "周九" };

    // Convert to list
    List list = new ArrayList(Arrays.asList(names));

    // Ensure list sorted
    Collections.sort(list);
    System.out.println("List排序: [length: " + list.size() + "]");
    System.out.println(list);

    // Search for element in list
    int index = Collections.binarySearch(list, "李四");
    System.out.println("發現位置 " + index);

    // Search for element not in list
    index = Collections.binarySearch(list, "、楚一");
    System.out.println("沒有發現楚一" + index);

    // Insert
    int newIndex = -index - 1;
    list.add(newIndex, "馮二");
    System.out.println("增加了馮二: [length: " + list.size()
        + "]");
    System.out.println(list);

    // Min should be Bart
    System.out.println(Collections.min(list));
    // Max should be Roy
    System.out.println(Collections.max(list));

    Comparator comp = Collections.reverseOrder();

    // Reversed Min should be Roy
    System.out.println(Collections.min(list, comp));
    // Reversed Max should be Bart
    System.out.println(Collections.max(list, comp));
  }
}
四、集合類的歷史

 

四、集合類的歷史

在早期JDK1.0版本中,代表集合的類只有VectorStackEnumerationHashtableProperties。從JDK1.2版本中開始,纔出現了CollectionSetListMap接口及各種實現類。它們構成了完整的集合框架。JDK10.版本中的集合類也稱歷史集合類

歷史集合類

歷史集合類

描述

缺點

新集合框架的替代類

Vector

集合中的元素有索引位置,在新的集合框架中把它改爲實現了List接口

採用了同步機制,影響操縱集合的性能

ArrayListLinkedList

Stack

表示堆棧,支持後進先出的操作

採用了同步機制,影響操縱集合的性能:Stack繼承了Vector類,使得Stack不能作爲嚴格的堆棧,還允許隨機訪問

LinkedList

Hashtable

集合中的每一個元素包含一對鍵與值。在新的集合框架中把它改爲實現了Map接口

採用了同步機制,影響操縱集合性能

HashMap

Properties

集合中的每個元素包含一對鍵與值,繼承了Hashtable

採用了同步機制,影響了操縱集合的性能

Enumeration

用於遍歷集合中元素

只能與VectorHashtable等歷史集合配套使用:Enumeration類的名字較長,沒有Iterator類名字簡短

Iterator

JDK1.2版本開始,對VectorHashtable做了修改,使它們分別實現了ListMap接口中。儘管如此,由於VectorStackHashtableEnumeration在實現中都使用了同步機制,併發性能差,因此不提倡使用它們,

 Properties類是一種特殊的Map類,它繼承了Hashtable(Object,Object)類。Properties類的load()方法可用來從輸入流中讀取鍵與值

實例12

myapp.properties

color=red
shape=circle
user=Tom


 

PropertiesTester.java

package collection;
import java.util.*;
import java.io.*;
public class PropertiesTester{
  public static void print(Properties ps){
    Set keys=ps.keySet();
    Iterator it=keys.iterator();
    while(it.hasNext()){
      String key=(String)it.next();
      String value=ps.getProperty(key);
//使用getProperty可以返回myapp.properties文件中的一個屬性
      System.out.println(key+"="+value);
    }
  }
  public static void main(String args[])throws IOException{
    Properties ps=new Properties();
    //myapp.properties文件與PropertiesTester類的.class文件位於同一個目錄下 
    InputStream in=PropertiesTester.class.getResourceAsStream("myapp.properties");
    ps.load(in);//把輸入流中的數據加載到Properties對象中
    print(ps);

    ps=System.getProperties();//該方法返回一個Properties對象,在這個對象中包含了一系列的系統屬性
    print(ps);
  }
}


 

小結:

我們介紹了幾種常用Java集合類的特性和使用方法。爲了保證集合正常工作,有些集合類對存放的對象有特殊的要求:

HashSetHashMap具有好的性能,SetMap首選的實現類,只有在需要排序的場合,才考慮用TreeSetTreeMapLinkedListArrayList各有優缺點,如果經常對元素執行插入和刪除操作,那麼可用LinkedList,如果經常隨機訪問元素,那麼可用ArrayList.

課後習題:

1.       SetList有哪些區別?

2.       CollectionCollections有什麼區別?

3.       比較Java數組、ArrayListLinkedList在查詢和存取元素方面的性能

4.       HashMapHashTable的區別

5.       編寫一個程序,讀入一系列名字並將它們存儲在LinkedList中,不能存儲重複的名字,並允許用戶查找一個名字。

 

附錄

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章