深入學(xué)習(xí)java系列之集合框架

集合概述

java 集合部分主要有 java 集合框架相關(guān)的內(nèi)容和 java 泛型相關(guān)的內(nèi)容兼丰。

集合是用來做什么的呢秸架,集合主要是用來對(duì)現(xiàn)實(shí)世界中多個(gè)對(duì)象在一起進(jìn)行統(tǒng)一描述的础爬。在現(xiàn)實(shí)世界中叠赦,常常我們會(huì)對(duì)多個(gè)在一起的對(duì)象進(jìn)行操作描述痘拆,比如1000學(xué)生的資料,100個(gè)用戶的資料等等喊废,基本的操作有增祝高、刪、改污筷、查」す耄現(xiàn)實(shí)中對(duì)這些對(duì)象的操作通常都是要進(jìn)行統(tǒng)計(jì)相關(guān)的操作,比如排序等操作瓣蛀。數(shù)組雖然也是對(duì)對(duì)象的統(tǒng)一描述陆蟆,在語義上差不多,但是在java語言使用中數(shù)組和集合仍有許多不同:

1.數(shù)組的長(zhǎng)度是固定的惋增,集合的長(zhǎng)度是可變的叠殷,
2.數(shù)組的可以存儲(chǔ)基本數(shù)據(jù)類型和對(duì)象,集合只能存儲(chǔ)對(duì)象诈皿,
3.jvm 底層的實(shí)現(xiàn)也是不同的林束,數(shù)組在 jvm 中有專門的指令和類型來支持,數(shù)組實(shí)例中有包含數(shù)組長(zhǎng)度 length 這個(gè)元素的單元稽亏。

集合框架中常用類關(guān)系圖

java集合.png

集合框架的子類 ArrayList壶冒、 LinkedList、 HashSet 截歉、TreeSet 胖腾、HashMap、 TreeMap 的主要區(qū)別是存儲(chǔ)方式(數(shù)據(jù)結(jié)構(gòu))不同瘪松。Collection 和 Map 的區(qū)別是:Collection是單列集合咸作,Map是雙列結(jié)合,是保存映射關(guān)系的集合

List 和 Set 的區(qū)別是:List 元素是有序的(存入取出的順序一致)宵睦,元素有索引性宏,并且元素可以重復(fù),Set 元素是沒有序的状飞,元素沒有索引毫胜,并且元素不能重復(fù)书斜。

Collection <E> 中的共性關(guān)系

增:
add(E e)
addAll(Collection<? extendsE> c)

刪:
clear()
remove(Object o)
removeAll(Collection<?> c) 去除交集
retainAll(Collection<?> c) 保留交集

改:
none

查:
iterator() 遍歷集合,獲取集合中的元素的引用
contains(Object o) 
containsAll(Collection<?> c) 
isEmpty()
size()

轉(zhuǎn)成數(shù)組:
toArray()

Iterator 接口

迭代器是用于取出集合中的元素酵使,這里迭代器類和集合類是相互依賴的關(guān)系荐吉,在創(chuàng)建迭代器對(duì)象的時(shí)候,需要傳一個(gè)集合類對(duì)象的引用口渔,迭代器才能知道遍歷那個(gè)對(duì)象样屠。而且具體的迭代器對(duì)象遍歷集合類的方法依賴于具體的集合類的數(shù)據(jù)結(jié)構(gòu),每個(gè)集合類中的迭代器是典型的內(nèi)部類的例子缺脉,把每個(gè)集合中迭代器共性抽取出來就有了 Iterator 接口痪欲。Iterator 和 Collection 的是典型的雙向關(guān)聯(lián)關(guān)系。

hasNext() 判斷是否有下一個(gè)元素
next() 獲取元素
remove() 移除元素

Iterator 接口的實(shí)現(xiàn)都是在 Collection 子類的內(nèi)部攻礼,是內(nèi)部類,迭代器使用注意业踢,在迭代過程中不可以使用集合的對(duì)象的方法來操作對(duì)象。

List <E> 集合的共性關(guān)系

增:
add(int index,E element)  把元素添加在 List 中指定索引的位置
addAll(int index,Collection<? extendsE> c) 把 Collection 集合添加在 List 中指定索引的位置

刪:
remove(int index) 刪除指定索引位置的元素

改:
set(int index,E element) 設(shè)置 List 中指定索引位置的元素礁扮,設(shè)置 List 中的元素引用

查:
indexOf(Object o) 獲取指定對(duì)象的第一個(gè)索引
lastIndexOf(Object o) 獲取指定對(duì)象最后一個(gè)索引
get(int index) 獲取索引位置的元素
listIterator(int index) 獲取指定位置開始的 List 的 ListIterator
subList(int fromIndex, int toIndex) 返回子 List

總結(jié):凡是可以操作角標(biāo)的方法都是 List 中特有的方法

Listiterator 接口

ListIterator 和 Iterator 的區(qū)別主要是知举, Iterator 只能進(jìn)行單向迭代,只能進(jìn)行刪除操作太伊,ListIterator 運(yùn)行雙向迭代 和 索引的迭代雇锡,并且可以在迭代過程中添加、刪除僚焦、修改元素

hasNext()
hasPrevious()
next()
nextIndex()
previous()
previousIndex()
add()
remove()
set()

List集合中常見之類對(duì)象 ArrayList LinkedList Vector 的特點(diǎn)

ArrayList:底層使用的是數(shù)組數(shù)據(jù)結(jié)構(gòu)锰提,特點(diǎn)是查詢和修改速度快,增刪比較慢芳悲,線程不同步
LinkedList:底層使用的是鏈表數(shù)據(jù)結(jié)構(gòu)立肘,特點(diǎn)是增刪速度很快,查詢和修改速度較慢
Vector:底層是數(shù)組數(shù)據(jù)結(jié)構(gòu)芭概。Vector 是線程同步的赛不。被 ArrayList 替代惩嘉。Vector 中的 Enumeration 接口的實(shí)現(xiàn)罢洲,和 Iterator 接口功能是重復(fù)的。

LinkedList 特有的方法

addFirst() push
addLast()
getFirst() peek
getLast()

如果集合中沒有元素文黎,會(huì)出現(xiàn) NoSuchElementException()惹苗,但是 peek 方法不會(huì),這是 JDK1.6 才有的方法耸峭,把 LinkedList 對(duì)象當(dāng)做 First 為棧頂?shù)亩褩?/p>

removeFirst() pull
removeLast()

如果集合中沒有元素桩蓉,會(huì)出現(xiàn) NoSuchElementException(),但是 pull 方法不會(huì)

使用 LinkedList 實(shí)現(xiàn)堆棧和隊(duì)列

/**
* 需求:創(chuàng)建一個(gè)隊(duì)列和堆棧的類
*/
public class HeapStackDemo {

    public static void main(String[] args) {
        Stack<String> stack = new Stack<String>();
        stack.push("1");
        stack.push("2");
        stack.push("3");
        System.out.println(stack.pull());
        System.out.println(stack.pull());
        System.out.println(stack.pull());
        System.out.println("-----------------------");
        Queue<String> queue = new Queue<String>();
        queue.push("1");
        queue.push("2");
        queue.push("3");
        System.out.println(queue.pull());
        System.out.println(queue.pull());
        System.out.println(queue.pull());
    }

}
class Stack<T> {

      private LinkedList<T> linkedList = new LinkedList<T>();
      
      public void push(T element) {
           linkedList.push(element);
      }

    public T pull() {
         return linkedList.pollFirst();
    }

}
class Queue<T>{

      private LinkedList<T> linkedList = new LinkedList<T>();

      public void push(T element) {
          linkedList.push(element);
      }

      public T pull() {
          return linkedList.pollLast();
      }

}

Set集合

Set集合的功能和 Collection 是一致的劳闹。

HashSet集合

HashSet:底層數(shù)據(jù)結(jié)構(gòu)是 hash 表院究,hashing定義了一種將字符組成的字符串轉(zhuǎn)換為固定長(zhǎng)度(一般是更短長(zhǎng)度)的數(shù)值或索引值的方法洽瞬,稱為散列法,也叫哈希法业汰;hash表伙窃,有時(shí)候也被稱為散列表,保存 hash 值的表就叫 hash 表样漆。

個(gè)人認(rèn)為为障,hash表是介于鏈表和二叉樹之間的一種中間結(jié)構(gòu)。鏈表使用十分方便放祟,但是數(shù)據(jù)查找十分麻煩鳍怨;二叉樹中的數(shù)據(jù)嚴(yán)格有序,但是這是以多一個(gè)指針作為代價(jià)的結(jié)果跪妥。hash表既滿足了數(shù)據(jù)的查找方便鞋喇,同時(shí)不占用太多的內(nèi)容空間,使用也十分方便骗奖。

打個(gè)比方來說确徙,所有的數(shù)據(jù)就好像許許多多的書本。如果這些書本是一本一本堆起來的执桌,就好像鏈表或者線性表一樣鄙皇,整個(gè)數(shù)據(jù)會(huì)顯得非常的無序和凌亂,在你找到自己需要的書之前仰挣,你要經(jīng)歷許多的查詢過程伴逸;而如果你對(duì)所有的書本進(jìn)行編號(hào),并且把這些書本按次序進(jìn)行排列的話膘壶,那么如果你要尋找的書本編號(hào)是n错蝴,那么經(jīng)過二分查找,你很快就會(huì)找到自己需要的書本颓芭;但是如果你每一個(gè)種類的書本都不是很多顷锰,那么你就可以對(duì)這些書本進(jìn)行歸類,哪些是文學(xué)類亡问,哪些是藝術(shù)類官紫,哪些是工科的,哪些是理科的州藕,你只要對(duì)這些書本進(jìn)行簡(jiǎn)單的歸類束世,那么尋找一本書也會(huì)變得非常簡(jiǎn)單,比如說如果你要找的書是計(jì)算機(jī)方面的書床玻,那么你就會(huì)到工科一類當(dāng)中去尋找毁涉,這樣查找起來也會(huì)顯得麻煩。

上面提到的歸類方法其實(shí)就是hash表的本質(zhì)锈死。HashSet 是把對(duì)象的 HashCode() 方法返回的 Hash 值作為類別存儲(chǔ)在 Hash 表中贫堰。HashSet 中的判斷是否是相同元素的方法就是先判斷元素是否是同一個(gè)類別穆壕,即 hashCode 值是否相同,再判斷元素是否相等其屏,即 equals 方法返回的結(jié)果粱檀。所以一般使用 HashSet 來保存某對(duì)象的集合,那么一般需要復(fù)寫該對(duì)象的 hashCode 方法和 equals 方法漫玄。equals 判斷的時(shí)候茄蚯,需要遍歷,比較低效睦优。查找都是基于比較的基礎(chǔ)之上的渗常,hashcode 這種查找元素比較的方法和 許多字符串比較的時(shí)候分段比較有異曲同工之妙。

String 常數(shù)保存在代碼區(qū)的常數(shù)區(qū)汗盘,所以如果相同的字符串常數(shù)皱碘,一定是對(duì)應(yīng)同一個(gè)常數(shù)區(qū)的對(duì)象,此時(shí)他們地址相同隐孽,并且內(nèi)容相同癌椿。

HashSet中保存自定義對(duì)象實(shí)例

/**
需求:演示 HashSet 中保存自定義對(duì)象集合
思路:
1.添加 Person 的對(duì)象到 HashSet 對(duì)象中
2.添加重復(fù)元素試試看,
3.定義 HashSet 對(duì)象中 Person 元素名字和年齡相同的元素就為相同元素
4.打印出 HashSet 對(duì)象中的元素

總結(jié):
1.Set 集合中菱阵,不能保存重復(fù)的元素踢俄,不同的存儲(chǔ)結(jié)構(gòu)對(duì)于元素是否相同的判斷方法不同
2.HashSet 對(duì)象中元素相同的定義依賴于元素的 hashCode 和 equals 方法
3.HashSet 中,判斷 hash 值是否相同底層實(shí)現(xiàn)可以用二叉樹結(jié)構(gòu)實(shí)現(xiàn)
*/

import java.util.*;

class HashSetDemo{

        public static void main(String[] args) {
            HashSet personHashSet = new HashSet();
            personHashSet.add(new Person("zhangsan",12));
            personHashSet.add(new Person("lisi",13));
            personHashSet.add(new Person("wangwu",14));
            personHashSet.add(new Person("zhaoliu",15));
            personHashSet.add(new Person("zhaoliu",15));
            Iterator iterator = personHashSet.iterator();
            while(iterator.hasNext()) {
                Person person = (Person)iterator.next();
                printObject(person);
            }
}

static void printObject(Object o) {
        System.out.println(o);
}

}
class Person

{

private String name;

private int age;

Person(String name, int age)

{

this.name = name;

this.age = age;

}

public String getName()

{

return name;

}

public int getAge()

{

return age;

}

public int hashCode()

{

System.out.println(name + ": hashCode");

return name.hashCode() + age*21;

}

public boolean equals(Object o)

{

if(o instanceof Person)

{

Person person = (Person)o;

System.out.println(name + ": equals :" + person.getName());

if(name.equals(person.getName()) && age == person.getAge())

{

return true;

}

}

return false;

}

public String toString()

{

return name + ":" + age;

}

}

TreeSet集合

通過把字符串對(duì)象集合保存在 TreeSet 對(duì)象中晴及,發(fā)現(xiàn) TreeSet 對(duì)象可以對(duì)字符串對(duì)象集合進(jìn)行自動(dòng)排序都办,通過往 TreeSet 對(duì)象中保存自定義對(duì)象發(fā)現(xiàn),TreeSet 對(duì)象中保存的元素必須是 Comparable 接口的對(duì)象虑稼。就是元素必須是可以比較大小的琳钉。當(dāng)比較結(jié)果是相等的時(shí)候,TreeSet 就認(rèn)為這兩個(gè)對(duì)象時(shí)相同對(duì)象蛛倦,只保存之前的那個(gè)歌懒。TreeSet 判斷集合中的元素的大小和是否相等,都依賴于元素所屬類的 Comparable 接口的實(shí)現(xiàn)函數(shù) compareTo()溯壶,或者傳給 TreeSet 的 Comparator 對(duì)象及皂。

TreeSet 保存自定義類型對(duì)象的集合實(shí)例

/**

需求:演示 TreeSet 中保存自定義對(duì)象

思路:

1.往 TreeSet 集合中存儲(chǔ)自定義對(duì)象學(xué)生,按照學(xué)生的年齡進(jìn)行排序

2.打印 TreeSet 集合中的元素

步驟:

總結(jié):

Set:無序茸塞,不可以有重復(fù)元素

|--HashSet:底層數(shù)據(jù)結(jié)構(gòu)是 hash 表躲庄,線程非同步

保證元素唯一性的原理:依賴元素的 hashCode 方法和 equals 方法返回的值

|--TreeSet:可以對(duì) Set 集合中的元素進(jìn)行自定義的排序查剖。

底層數(shù)據(jù)結(jié)構(gòu)是二叉樹

保證元素唯一性的依據(jù)之一:元素的 compareTo 方法

TreeSet 排序的第一種方式:讓元素自身具備比較性钾虐,元素需要實(shí)現(xiàn) Comparable 接口

TreeSet 排序的第二種方式:傳一個(gè) Comparator 實(shí)例給 TreeSet,讓容器自身具備比較性

就好像一個(gè)班上的人可以自動(dòng)按身高排序笋庄,這個(gè)可以算作他們自身具備的比較性效扫,也可以

給老師一個(gè)直尺倔监,老師量過每個(gè)人的升高后,老師來給這些人排序菌仁。

Comparator 和 Comparable 接口使得 TreeSet 集合具有了極好的擴(kuò)展性浩习。

*/

import java.util.*;

class TreeSetDemo

{

public static void main(String[] args)

{

//TreeSet studentTreeSet = new TreeSet(); // 使用自身比較性排序

TreeSet studentTreeSet = new TreeSet(new StudentComparator()); //使用比較器排序

studentTreeSet.add(new Student("zhangsan", 22));

studentTreeSet.add(new Student("lisi", 17));

studentTreeSet.add(new Student("wangwu", 18));

studentTreeSet.add(new Student("zhaoliu", 18));

Iterator studentIterator = studentTreeSet.iterator();

while(studentIterator.hasNext())

{

Student student = (Student)studentIterator.next();

System.out.println(student);

}

}

}
class Student implements Comparable

{

private String name;

private int age;

Student(String name, int age)

{

this.name = name;

this.age = age;

}

public String getName()

{

return name;

}

public int getAge()

{

return age;

}

public int compareTo(Object o)

{

if(o instanceof Student)

{

Student student = (Student)o;

System.out.println(toString() + "...compareTo:..." + student.toString());

if(age == student.getAge())

{

return name.compareTo(student.getName());

}

else

{

return ((Integer)age).compareTo(student.getAge());

}

}

return -1;

}

public String toString()

{

return name + ":" + age;

}

}
class StudentComparator implements Comparator

{

public int compare(Object object1, Object object2)

{

Student stu1 = (Student)object1;

Student stu2 = (Student)object2;

int num = stu1.getName().compareTo(stu2.getName());

if(num == 0)

{

return ((Integer)stu1.getAge()).compareTo((Integer)stu2.getAge());

}

return num;

}

}

comparable、comparator 和 TreeSet 济丘、集合中元素 的關(guān)系再次說明谱秽,拆分某個(gè)功能的時(shí)候,有兩種方法摹迷,一種是以模板方法的模式疟赊,讓拆分出來的功能在子類中以重寫的形式存在,拆分出來的功能可以是父類中的抽象函數(shù)峡碉,也可以是接口中的抽象函數(shù)近哟,和原系統(tǒng)形成父子類的關(guān)系,比如 comparable 和 集合中元素的關(guān)系鲫寄;另一種是讓拆分出來的功能和原系統(tǒng)以組合的形式存在吉执,比如 comparator 和 TreeSet 的關(guān)系。

泛型

JDK1.5 以后出現(xiàn)的新特性地来,用于解決安全問題的戳玫,是一個(gè)安全機(jī)制。JDK升級(jí)有3種目的未斑,高效量九、安全、簡(jiǎn)化書寫颂碧。

范型荠列,就是帶有數(shù)據(jù)類型參數(shù)的數(shù)據(jù)類型,在類定義的時(shí)候载城,不能明確類中某個(gè)部分?jǐn)?shù)據(jù)的類型肌似,那么就可以使用泛型機(jī)制,用一個(gè)標(biāo)識(shí)符來代替這個(gè)類型诉瓦,并用 <> 聲明這是一種類型代替機(jī)制川队,當(dāng)使用這個(gè)類的時(shí)候才指定這個(gè)類型,這就是泛型睬澡。

泛型的好處:

1.將運(yùn)行時(shí)期出現(xiàn)的的問題 ClassCastException固额,轉(zhuǎn)移到編譯時(shí)期,方便程序猿解決問題煞聪,讓運(yùn)行時(shí)期問題減少斗躏,增加安全性。

2.避免了強(qiáng)制轉(zhuǎn)換的麻煩和不安全問題昔脯。

在使用泛型類的時(shí)候啄糙, < > 就是用來接收類型的笛臣,將類中將要使用的數(shù)據(jù)類型,作為參數(shù)傳遞到 < > 即可隧饼,如同函數(shù)調(diào)用的時(shí)候參數(shù)傳遞一樣沈堡,但是 < > 中只能接收引用數(shù)據(jù)類型。

泛型的定義可以在兩個(gè)地方定義燕雁,就是 泛型類和泛型方法诞丽。

泛型類

/**

需求:演示泛型類的定義和使用

思路:定義一個(gè)工具類主要功能是可以保存某個(gè)對(duì)象實(shí)例到成員變量中,還支持

把該對(duì)象實(shí)例取出來拐格,但是為了能操作多種數(shù)據(jù)類型的對(duì)象率拒,該引用數(shù)據(jù)類型延

遲到在類使用的時(shí)候才確定,而類的使用有兩種禁荒,一種是使用類定義變量猬膨、創(chuàng)建

對(duì)象,一種是 extends 該類

步驟:

總結(jié):

1.泛型類就是具有泛型聲明的類呛伴,也就是泛型定義在類上勃痴,使用范圍是該類的內(nèi)部方法、成員變量和局部變量

2.什么時(shí)候定義泛型類:在定義一個(gè)類的時(shí)候热康,當(dāng)類中要操作的引用類型數(shù)據(jù)在

類定義的時(shí)候數(shù)據(jù)類型不能確定(比如想要支持多種數(shù)據(jù)類型)沛申,而在類使用的

時(shí)候可以確定,早期使用 Object 來實(shí)現(xiàn)姐军,現(xiàn)在定義泛型類來實(shí)現(xiàn)铁材。

*/

class GenericClassDemo

{

public static void main(String[] args)

{

Utils<Worker> util = new Utils<Worker>();

util.setObject(new Worker());

//util.setObject(new Student()); // 編譯時(shí)就會(huì)發(fā)現(xiàn)問題

Worker worker = util.getObject();

}

}
class Utils<T>

{

private T object;

public void setObject(T object)

{

this.object = object;

}

public T getObject()

{

return object;

}

}
class Worker

{

}
class Student

{

}

泛型類和使用 Object 的區(qū)別

/**

需求:一個(gè)釘子廠,釘子廠可以生產(chǎn)釘子和對(duì)外打廣告奕锌,釘子的原料可

以有3種著觉,鐵、鋁惊暴、銅饼丘,生產(chǎn)的時(shí)候用一種就行,一個(gè)釘子廠在實(shí)例化

的時(shí)候辽话,才定下來這個(gè)釘子廠只能用那一種原料生產(chǎn)肄鸽。

思路:

步驟:

*/

class NailFactoryDemo

{

public static void main(String[] args)

{

NailFactory<Aluminum> aluminumNailFactory = new NailFactory<Aluminum>();

aluminumNailFactory.advertise();

//aluminumNailFactory.makeNail(new Copper());

aluminumNailFactory.makeNail(new Aluminum());

}

}
class NailFactory<T>

{

public void advertise()

{

System.out.println("我們廠是世界上最好的釘子廠");

}

public void makeNail(T material)

{

if(material instanceof Aluminum || material instanceof Copper

|| material instanceof Ferrum)

{

System.out.println("生產(chǎn)出1噸 " + material + " 釘子");

}

else

{

return;

}

}

}

/*

class NailFactory

{

public void advertise()

{

System.out.println("我們廠是世界上最好的釘子廠");

}

public void makeNail(Object material)

{

if(material instanceof Aluminum || material instanceof Copper

|| material instanceof Ferrum)

{

System.out.println("生產(chǎn)出1噸 " + material + " 釘子");

}

else

{

return;

}

}

}

//*/

class Aluminum

{

public String toString()

{

return "鋁";

}

}

class Copper

{

public String toString()

{

return "銅";

}

}
class Ferrum

{

public String toString()

{

return "鐵";

}

}

class Gold

{

public String toString()

{

return "金";

}

}

泛型方法、靜態(tài)泛型方法

/**

需求:演示泛型方法油啤、靜態(tài)方法泛型

思路:定義一個(gè)工具類典徘,可以打印對(duì)象,但是為了能打印所有對(duì)象打印的對(duì)

象的類型延遲到方法調(diào)用的時(shí)候才能確定益咬。

步驟:

總結(jié):

1.為了讓不同方法可以操作不同引用類型逮诲,而且類型還要延遲到使用方法的

時(shí)候才能確定,那么泛型可以定義在方法上。

2.在調(diào)用泛型方法的時(shí)候汛骂,不用像使用泛型類一樣指定類型,而是根據(jù)方法

的參數(shù)列表和返回值來確定泛型的類型评腺,泛型方法相當(dāng)于帘瞭,方法參數(shù)的類型

或者方法返回值類型延遲到方法實(shí)際調(diào)用的時(shí)候才確定,并且不用顯式指定

蒿讥,泛型類型會(huì)根據(jù)返回值類型和參數(shù)類型默認(rèn)進(jìn)行類型匹配蝶念。

3.泛型類中的泛型類型作用于整個(gè)對(duì)象生存期,而泛型方法中的泛型類型只

作用于方法的某一次執(zhí)行期間芋绸,和方法的局部變量類似媒殉。

4.靜態(tài)方法不可以訪問類上定義的泛型,如果靜態(tài)方法操作的引用數(shù)據(jù)類型

不確定可以將泛型定義在方法上摔敛。

*/

class GenericMethodDemo

{

public static void main(String[] args)

{

Utils util = new Utils();

util.show("泛型");

util.show(new Integer(15));

util.print(new Integer(15));

}

}
class Utils

{

public <T> void show(T t) //作用范圍只在該方法中

{

System.out.println("show" + t);

}

public <T> void print(T t)

{

System.out.println("print" + t);

}

public static <T> void out(T t)

{

System.out.println("out" + t);

}

}

泛型接口

/**

需求:演示泛型接口

思路:假設(shè)一個(gè)妻子在做飯的方法中使用過的原材料一生中都是固定的廷蓉,并

且在該妻子創(chuàng)建的時(shí)候才確定原材料的類型。

步驟:

總結(jié):

1.接口的泛型是延遲接口中使用的引用類型到實(shí)現(xiàn)接口或者使用接口

定義變量的時(shí)候才確定马昙,如果實(shí)現(xiàn)接口的類還不能確定該引用類型桃犬,可以

繼續(xù)定義泛型類,延遲到類創(chuàng)建對(duì)象的時(shí)候才確定該類型行楞。所以延遲也是

可以傳遞的攒暇。

2.如果泛型類在使用的時(shí)候,如果指定泛型的類型子房,在使用該對(duì)象調(diào)用類

的函數(shù)的時(shí)候形用,才會(huì)提示類型不安全。

3.泛型在指定類型的時(shí)候证杭,看起來有點(diǎn)重復(fù)田度,因?yàn)轭惿弦付ǎ瘮?shù)上還

要傳對(duì)應(yīng)的參數(shù)解愤,如果有類的依賴關(guān)系每币,依賴的類在使用過的時(shí)候還要指

定,也就是在每一個(gè)使用泛型類的地方都要指定琢歇。

4.泛型增加了安全性兰怠,但是也增加了表達(dá)上的冗余。

*/

class GenericInterfaceDemo

{

public static void main(String[] args)

{

//Family family = new Family(new XiaoFang<String>()); //會(huì)報(bào)操作不安全

Family<String> family = new Family<String>(new XiaoFang<String>());

family.cook("米");

}

}

interface Wife<T> //T符號(hào)在此定義

{

void cook(T material); //T符號(hào)在此使用

}

/*

class Family

{

Wife wife;

Family(Wife wife)

{

this.wife = wife;

}

public void cook(Object material)

{

wife.cook(material);

}

}

//*/

class Family<T> //T符號(hào)在此定義

{

Wife<T> wife; //T符號(hào)在此使用李茫,使用Family的范型類型指定Wife的范型類型

Family(Wife<T> wife)

{

this.wife = wife;

}

public void cook(T material)

{

wife.cook(material);

}

}

class XiaoFang<T> implements Wife<T> //XiaoFang<T>中的符號(hào)T是表示聲明T是類型揭保,Wife<T>中的符號(hào)T是表示使用類型T

{

public void cook(T material)

{

System.out.println("XiaoFang cooking by " + material);

}

}

泛型通配符合泛型限定

泛型通配符,主要是指在定義類或魄宏、接口或者方法的時(shí)候秸侣,如果需要正在使用泛型,并且需要指定泛型的類型,那么可以不用指定泛型的類型味榛,而是使用通配符 椭坚? 來表示類型。使用通配符 搏色? 給泛型分配一個(gè)不定的類型善茎,在傳遞參數(shù)的時(shí)候,根據(jù)參數(shù)類型自動(dòng)確定類型频轿,通配符的好處在于自動(dòng)確定類型垂涯,壞處是在定義的函數(shù)或者類內(nèi)部不能使用通配符表達(dá)的類型來定義變量,同樣只能使用通配符來修飾泛型航邢。

泛型通配符主要是在我們進(jìn)行泛型傳遞的時(shí)候使用耕赘。

泛型限定,主要是指在定義泛型類或者泛型接口的時(shí)候膳殷,給泛型的類型 T 加上一個(gè)可以接受的類型的范圍限制操骡,上限使用 extends,比如 <T extends Object>赚窃,下限使用 super当娱,比如 <T super ArrayList>

使用通配符的時(shí)候還可以指定上下限,格式如下:

<? extends T> 上限

<? super T> 下限

泛型限定實(shí)例

/**

需求:演示泛型限定

思路:

1.演示 Comparator 接口中泛型限定的作用

2.定義一個(gè) Person 類和它的兩個(gè)子類 Student考榨、Worker

3.在兩個(gè) TreeSet 集合中分別保存 Student 和 Worker 對(duì)象

4.Person Student Worker 對(duì)象排序的定義都相同

5.Comparator 接口的是實(shí)現(xiàn)類中泛型分別用 Person Student Worker

6.比較三個(gè) Comparator 實(shí)現(xiàn)類的優(yōu)劣

步驟:

總結(jié):

因?yàn)?TreeSet 構(gòu)造函數(shù)的泛型限定 new TreeSet<E>(Comparator<? super E> c)

所以可以只定義 PersonComparator 比較器

new TreeSet<Student>(new PersonComparator())

new TreeSet<Worker>(new PersonComparator())

這樣就提高了代碼的復(fù)用性跨细,減少了代碼重復(fù)

printCollection(Collection<? extends Person> e) 方法也是相同的道理

*/

import java.util.*;

class GenericLimitDemo

{

public static void main(String[] args)

{

TreeSet<Student> studentTreeSet = new TreeSet<Student>(new PersonComparator());

studentTreeSet.add(new Student("aaa11"));

studentTreeSet.add(new Student("aaa13"));

studentTreeSet.add(new Student("aaa12"));

studentTreeSet.add(new Student("aaa14"));

TreeSet<Worker> workerTreeSet = new TreeSet<Worker>(new PersonComparator());

workerTreeSet.add(new Worker("aaa--11"));

workerTreeSet.add(new Worker("aaa--14"));

workerTreeSet.add(new Worker("aaa--12"));

workerTreeSet.add(new Worker("aaa--13"));

prinPersontCollection(studentTreeSet);

prinPersontCollection(workerTreeSet);

}

static void printPersonCollection(Collection<? extends Person> e)

{

Iterator<? extends Person> iterator = e.iterator();

while(iterator.hasNext())

{

Person person = iterator.next();

System.out.println(person.getName());

}

}

}

class Person

{

private String name;

Person(String name)

{

this.name = name;

}

public String getName()

{

return name;

}

}

class Student extends Person

{

Student(String name)

{

super(name);

}

}

class Worker extends Person

{

Worker(String name)

{

super(name);

}

}

class PersonComparator implements Comparator<Person>

{

public int compare(Person person1, Person person2)

{

return person2.getName().compareTo(person1.getName());

}

}

Map集合

Map 是保存映射關(guān)系的集合,是一種雙列集合河质,當(dāng)發(fā)現(xiàn)有映射關(guān)系集合需要描述的時(shí)候冀惭,選擇 Map 集合。

Map

|——HashTable:底層是 hash 表數(shù)據(jù)結(jié)構(gòu)掀鹅,不可以存入 null 鍵散休、null 值,該集合是線程同步的乐尊,jdk1.0 效率低下戚丸。

|——HashMap:底層是 hash 表數(shù)據(jù)結(jié)構(gòu),運(yùn)行存入 null 鍵扔嵌、null 值限府,該集合是線程不同步的,jdk1.2 效率高痢缎。

|——TreeMap:底層是二叉樹數(shù)據(jù)結(jié)構(gòu)胁勺,線程不同步,可以給 map 集合中的映射按鍵進(jìn)行排序独旷。

Map 其實(shí)和 Set 很像署穗,Set 是特殊的 Map寥裂,Set 底層就是用 Map 實(shí)現(xiàn)的。

Map 集合的主要操作方法有

增案疲、改

V put(K key, V value) 把指定值和鍵關(guān)聯(lián)封恰,并該關(guān)系保存在Map中,如果 put 的映射中的鍵已經(jīng)存在褐啡,則覆蓋已有的映射诺舔,并返回被覆蓋的映射的值。

void putAll(Map<? extends K, ? extends V> m)

void clear() 刪除所有映射

V remove(Object key) 刪除指定的鍵的映射

boolean containKey(Object key)

boolean containValue(Object value)

boolean isEmpty()

int size()

V get(Object key) 返回給定的鍵對(duì)應(yīng)的值

Set<K> keySet() 返回映射集合中所有鍵的集合

Set<Map.Entry<K, V>> entrySet() 返回所有映射的集合

Collection<V> values() 返回映射集合中包含的所有的值的集合

import java.util.*;

class MapDemo

{

public static void main(String[] args)

{

//建立Map集合

Map<String,String> map=new HashMap<String,String>();

////添加元素,添加元素春贸,如果出現(xiàn)添加時(shí)混萝,相同的鍵遗遵。那么后添加的值會(huì)覆蓋原有鍵對(duì)應(yīng)值萍恕。

map.put("01","zhangsan1");

map.put("02","zhangsan2");

map.put("03","zhangsan3");

map.put("04","zhangsan4");

sop(map.containsKey("02"));//判斷鍵是否存在

sop(map.get("01"));//可以通過get方法的返回值來判斷一個(gè)鍵是否存在。通過返回null來判斷车要。

sop(map);

Collection<String> coll=map.values();//用values獲取map集合中所有的值.

sop(coll);

}

public static void sop(Object obj)//為了打印方便建立一個(gè)函數(shù)

{

System.out.println(obj);

}

}

Map集合兩種遍歷取出方式

1.Set<K> keySet:將 Map 中所有的鍵存入到 Set 集合中允粤,因?yàn)?Set 具備迭代器,所以可以根據(jù)迭代方式取出所有的鍵翼岁,再使用 get 方法类垫,根據(jù)鍵獲取每一個(gè)鍵對(duì)應(yīng)的值。

是將 Map 集合中的 key 轉(zhuǎn)換為 Set 集合琅坡,再通過迭代器取出每個(gè) key悉患,再通過 key 取出每個(gè)映射的 value边败。

import java.util.*;

class MapDemo2

{

public static void main(String[] args)

{

Map<String,String> map=new HashMap<String,String>();

map.put("01","zhansan01");

map.put("02","zhansan02");

map.put("03","zhansan03");

map.put("04","zhansan04");

Set<String> s=map.keySet();//先獲取map集合的所有鍵的Set集合,keySet();

System.out.println(map);//有了Set集合起暮。就可以獲取其迭代器。

for(Iterator<String> it=s.iterator(); it.hasNext();)

{

String key=it.next();

//有了鍵可以通過map集合的get方法獲取其對(duì)應(yīng)的值墨微。

String value=map.get(key);

System.out.println(key+"..."+value);

}

}

}

2.Set<Map.Entry<K, V>> entrySet:將 Map 集合中的映射關(guān)系存入到了 Set 集合中茴晋,而這個(gè)關(guān)系的數(shù)據(jù)類型就是 Map.Entry陪捷。Map.Entry 也是一個(gè)接口,它是 Map 接口的一個(gè)內(nèi)部接口诺擅。

Map 和 Entry 的關(guān)系就是市袖,外部類和靜態(tài)成員內(nèi)部類的關(guān)系,因?yàn)?Entry 是 Map 的組成元素烁涌,總數(shù)伴隨著 Map 出現(xiàn)苍碟,所以把 Entry 放入了 Map 內(nèi)部。

interface Map<K, V>

{

public static interface Entry<K, V>

{

public abstract K getKey();

public abstract V getValue();

...

}

...

}

/**

1.描述學(xué)生.

2.定義map容器.將學(xué)生作為鍵.住址作為值,存入.

3.獲取map集合中的元素

*/

import java.util.*;

class MapDemo3

{

public static void main(String[] args)

{

Map<Integer,String> map=new HashMap<Integer,String>();

map.put(01,"java01");

map.put(02,"java02");

map.put(03,"java03");

map.put(04,"java04");

//將Map集合中的映射關(guān)系取出撮执。存入到Set集合中驰怎。

Set<Map.Entry<Integer,String>> entrySet=map.entrySet();

for(Iterator<Map.Entry<Integer,String>> it=entrySet.iterator();it.hasNext();)

{

Map.Entry<Integer,String> me=it.next();

Integer key=me.getKey();

String value=me.getValue();

System.out.println(key+"...."+value);

}

}

}

Map 中保存自定義類鍵值對(duì)

import java.util.*;

class Student implements Comparable<Student>// 為了以后方便可能存進(jìn)去TreeSet集合中去實(shí)現(xiàn)Comparable.將學(xué)生具備比較性

{

private String name;

private int age;

Student(String name, int age)// 將學(xué)生name和age初始化

{

this.name = name;

this.age = age;

}

public int compareTo(Student s)// 覆蓋Comparable中的compareTo方法.來比較,先比較age在比較name.

{

int num = new Integer(this.age).compareTo(new Integer(s.age));// Integer是因?yàn)閍ge是基本數(shù)據(jù)類型不具備比較.要轉(zhuǎn)成Integer來實(shí)現(xiàn)compareTo方法

if (num == 0)

return this.name.compareTo(s.name);// name是字符串本身就比較比較性.直接使用compareTo方法就哦了

return num;

}

public int hashCode()// 復(fù)寫hashCode.來讓存進(jìn)去的學(xué)生保證唯一.為什么要覆蓋因?yàn)槟J(rèn)比較是比較hash值.和內(nèi)容是否一樣.因?yàn)榇娴氖莌ash值所以每次建立對(duì)象的時(shí)候都不一樣

// 所以要復(fù)寫hashCode,來比較年齡和姓名是否是相同.這樣就能保證學(xué)生的唯一性了.

{

return name.hashCode() + age * 34;

}

public boolean equals(Object obj)// 如果年齡相同的話在比較年齡.

{

if (!(obj instanceof Student))// 如果穿進(jìn)去不的不是學(xué)生類.拋異常

throw new ClassCastException("類型不匹配");

Student s = (Student) obj;

return this.name.equals(s.name) && this.age == s.age;

}

public String getName() {

return name;

}

public int getInt() {

return age;

}

public String toString() {

return name + ".." + age;

}

}
class MapTest {

public static void main(String[] args) {

HashMap<Student, String> hm = new HashMap<Student, String>();// 穿件Map集合中的

// HashMap集合

hm.put(new Student("zhangsan01", 21), "beijing");// 往里面添加鍵值對(duì).將學(xué)生作為鍵.住址作為值

hm.put(new Student("zhangsan02", 22), "tianjing");

hm.put(new Student("zhangsan03", 23), "sahgnhan");

hm.put(new Student("zhangsan01", 23), "sahgnhan");

// 第一種去取出方式

Set<Student> s = hm.keySet();

for (Iterator<Student> it = s.iterator(); it.hasNext();) {

Student key = it.next();

String value = hm.get(key);

System.out.println(key + "...." + value);

}

// 第二種取出方式

System.out

.println("-----------------------------------------------------");

Set<Map.Entry<Student, String>> entrySet = hm.entrySet();

for (Iterator<Map.Entry<Student, String>> it = entrySet.iterator(); it

.hasNext();) {

Map.Entry<Student, String> me = it.next();

Student key1 = me.getKey();

String value1 = me.getValue();

System.out.println(key1 + "..." + value1);

}

}

}
```java
字母?jìng)€(gè)數(shù)統(tǒng)計(jì)Map描述實(shí)例

集合框架的工具類

Collections :給 Collection 提供工具方法,類中沒有對(duì)象特有數(shù)據(jù)二打,所以都是靜態(tài)方法县忌,主要是給 List 提供眾多集合操作工具掂榔。

sort:對(duì)List集合排序,支持可重復(fù)元素的排序

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

根據(jù)元素的自然順序 對(duì)指定列表按升序進(jìn)行排序症杏。列表中的所有元素都必須實(shí)現(xiàn) Comparable 接口

Collections.sort(list);

不可以給Set排序因?yàn)镾et里面有TreeSet.用它也沒什么用

如果出現(xiàn)了重復(fù)元素也可以排序,因?yàn)槭菙?shù)組結(jié)構(gòu)有索引.

Collections.sort(list,new 比較器Comparator);

如果不喜歡默認(rèn)的排序可以自定義比較器(比如最大長(zhǎng)度)

max(Collection c) 按元素默認(rèn)比較性取出集合中的最大元素

public static <T> T max(Collection<? extends T> coll,Comparator<? super T> comp)

根據(jù)指定比較器產(chǎn)生的順序装获,返回給定 collection 的最大元素。collection 中的所有元素都必須可通過指定比較器相互比較

binarySearch(List list, K key) 對(duì)列表進(jìn)行二分查找厉颤,獲取指定元素穴豫,返回排序后該元素的index,

public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)

使用二分搜索法搜索指定列表逼友,以獲得指定對(duì)象精肃。在進(jìn)行此調(diào)用之前,必須根據(jù)列表元素的自然順序

對(duì)列表進(jìn)行升序排序(通過 sort(List) 方法)帜乞。如果沒有對(duì)列表進(jìn)行排序司抱,則結(jié)果是不確定的。如果列

表包含多個(gè)等于指定對(duì)象的元素黎烈,則無法保證找到的是哪一個(gè)习柠。

如果搜索鍵包含在列表中,則返回搜索鍵的索引照棋;否則返回 (-(插入點(diǎn)) - 1)资溃。

插入點(diǎn) 被定義為將鍵插入列表的那一點(diǎn):即第一個(gè)大于此鍵的元素索引;如果列表中的所有元素都

小于指定的鍵烈炭,則為 list.size()溶锭。注意,這保證了當(dāng)且僅當(dāng)此鍵被找到時(shí)符隙,返回的值將 >= 0趴捅。

Collection.sort(list);//對(duì)list集合進(jìn)行排序

int index =Collection.binarySearch(list,"aaa");//查找"aaa"所在位置.返回插入點(diǎn)-1

Collection.sort(list,new 比較器Comparator);//對(duì)list集合進(jìn)行長(zhǎng)度排序

int index =Collection.binarySearch(list,"aaa"new 比較器Comparator);//查找"aaa"所在位置.(按長(zhǎng)度來了)返回插入點(diǎn)-1

copy(List dest, List src) 復(fù)制一個(gè) List 集合到另一個(gè) List 集合

public static <T> void fill(List<? super T> list,T obj) 使用指定元素替換 List 集合中所有元素

public static <T> boolean replaceAll(List<T> list,T oldVal,T newVal) 將集合中某個(gè)值元素全部替換為新值

swap(List l,int x, int y)

reverse(List list) 反轉(zhuǎn) list 中元素的順序

shuffle(List l) 隨機(jī)排列 List 中的元素

reverseOrder() 返回逆向比較器

同步集合類對(duì)象

synchronizedCollection (Collection c)

synchronizedList(List c)

synchronizedSet(Set c)

synchronizedMap (Map c)

Arrays

用于操作數(shù)組的工具類,里面都是靜態(tài)方法

binarySearch() 二分查找

copyOf() 復(fù)制數(shù)組

equals() 比較數(shù)組是否相同

fill() 替換數(shù)組中的值

sort() 給數(shù)組中元素排序

toString()

asList() 把數(shù)組轉(zhuǎn)為 List 集合膏执,把數(shù)字變?yōu)?List 集合的好處是驻售,可以用集合的思想和方法來操作數(shù)組中的元素,比如 contains() 方法判斷元素是否存在更米。數(shù)組變集合后不支持 add 函數(shù)欺栗,因?yàn)閿?shù)組長(zhǎng)度是固定的。如果你增刪征峦。那么發(fā)生 UnsupportedOperationException迟几。

數(shù)組變集合,如果數(shù)組中是基本類型元素栏笆,則把數(shù)組作為集合的元素类腮,如果是數(shù)組是引用類型元素,則直接把數(shù)組元素作為集合元素蛉加。

集合變數(shù)組用集合的 toArray() 函數(shù)蚜枢。

contains

get

indexOf()

subList() 等集合的操作方法缸逃。

集合變數(shù)組:

Collection接口中的toArray方法

1.指定類型的數(shù)組到底是要定義多長(zhǎng)呢?

  當(dāng)指定類型的數(shù)組長(zhǎng)度小于集合的size.那么該方法內(nèi)部會(huì)創(chuàng)建一個(gè)新的數(shù)組.長(zhǎng)度為集合的size.

  當(dāng)指定類型的數(shù)據(jù)長(zhǎng)度大于集合的size.就不會(huì)創(chuàng)建新的數(shù)組.而是使用傳遞進(jìn)來的數(shù)組.

  所以創(chuàng)建一個(gè)剛剛好的數(shù)組最優(yōu).

 2.為什么要將集合變成數(shù)組呢?

  為了限定對(duì)元素的操作, 不需要進(jìn)行增刪了.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市厂抽,隨后出現(xiàn)的幾起案子需频,更是在濱河造成了極大的恐慌,老刑警劉巖筷凤,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昭殉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡藐守,警方通過查閱死者的電腦和手機(jī)挪丢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卢厂,“玉大人乾蓬,你說我怎么就攤上這事∽阆” “怎么了巢块?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵礁阁,是天一觀的道長(zhǎng)巧号。 經(jīng)常有香客問我,道長(zhǎng)姥闭,這世上最難降的妖魔是什么丹鸿? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮棚品,結(jié)果婚禮上靠欢,老公的妹妹穿的比我還像新娘。我一直安慰自己铜跑,他們只是感情好门怪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锅纺,像睡著了一般掷空。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上囤锉,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天坦弟,我揣著相機(jī)與錄音,去河邊找鬼官地。 笑死酿傍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驱入。 我是一名探鬼主播赤炒,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氯析,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了莺褒?” 一聲冷哼從身側(cè)響起魄鸦,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎癣朗,沒想到半個(gè)月后拾因,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旷余,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年绢记,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片正卧。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蠢熄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炉旷,到底是詐尸還是另有隱情签孔,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布窘行,位于F島的核電站饥追,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏罐盔。R本人自食惡果不足惜但绕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惶看。 院中可真熱鬧捏顺,春花似錦、人聲如沸纬黎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽本今。三九已至拆座,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诈泼,已是汗流浹背懂拾。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铐达,地道東北人岖赋。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瓮孙,于是被迫代替她去往敵國(guó)和親唐断。 傳聞我的和親對(duì)象是個(gè)殘疾皇子选脊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355