第十章 集合(8)

了解:自定义排序算法:https://www.jianshu.com/p/9334567d0912

集合和数组的区别

数组:定长的 可以存放相同类型的数据 数据可以是基本类型也可以是引用类型 访问速度比较快
集合:可变长 只能存放引用类型 访问速度比数组会稍慢一些 java.util包里

集合层次结构

image.png

集合分类

collection set list map 接口

set list map区别

set:无序的,不重复 用来去重
list:有序的,数据可以重复
map: key-value键值对方式存储的 key 不可以重复,value可以重复

hashSet按hashcode排序(无序)

 @Test
  public void test1(){
      //先实例化
      HashSet s=new HashSet();
      //存放数据
      s.add("liucy");
      s.add("tanzy");
      s.add("liucy");
      s.add("zhanghs");
      s.add("shenp");
      //读出并打印
      for(Object name:s){
          System.out.println(name);
      }
  }

TreeSet按自然顺序(字母a-z,中文全拼顺序)

  @Test
  public void test2(){
      //先实例化
      TreeSet s=new TreeSet();
      //存放数据
      s.add("liucy");
      s.add("tanzy");
      s.add("liucy");
      s.add("zhanghs");
      s.add("shenp");
      //读出并打印
      for(Object name:s){
          System.out.println(name);
      }
  }


@Test
  public void test3(){
      //先实例化
      HashSet s=new HashSet();
      //存放数据
      s.add("liucy");
      s.add("tanzy");
      s.add("liucy");
      s.add("zhanghs");
      s.add("shenp");
      //沈盼出去了
      s.remove("shenp");
      //查看有没有指定元素
      boolean result=s.contains("shenp");
      System.out.println(result);
      //读出并打印
      for(Object name:s){
          System.out.println(name);
      }
  }

用泛型限制集合数据的类型
<数据类型>

  @Test
  public void test4(){
      //先实例化
      HashSet<String> s=new HashSet();
      //存放数据
      s.add("liucy");
      s.add("liucy");
      s.add("shenp");
      //沈盼出去了
      s.remove("shenp");
      //查看有没有指定元素
      boolean result=s.contains("shenp");
      System.out.println(result);
      //读出并打印
      for(String name:s){
          System.out.println(name);
      }
  }

retainAll 保留子集

@Test
    public void test6(){
         Set<String> s=new HashSet();
        s.add("qinggx");
        s.add("zhougb");
        s.add("dddd");
        s.add("ccccc");
        System.out.println(s);
         Set<String> white=new HashSet();
        white.add("qinggx");
        white.add("zhougb");
        //把白名单里的人保留下来
        s.retainAll(white);
        System.out.println(s);
}

removeAll

@Test
public void test5(){
    Set<String> s=new HashSet();
    s.add("qinggx");
    s.add("zhougb");
    s.add("dddd");
    s.add("ccccc");
    System.out.println(s);
    Set<String> black=new HashSet();
    black.add("qinggx");
    black.add("zhougb");
    //把黑名单里的人从s里移除
    s.removeAll(black);
    System.out.println(s);
}

存放实体类

public class ClassA {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

测试方法

  @Test
  public void test5(){
      ClassA a=new ClassA();
      a.setId(16);
      a.setName("java16");
      ClassA b=new ClassA();
      b.setId(7);
      b.setName("java7");
      //先实例化
      HashSet<ClassA> s=new HashSet();
      s.add(a);
      s.add(b);
      for(ClassA c:s){
          System.out.println(c.getId()+" "+c.getName());
      }
      
  }

Iterator 迭代器#

@Test
public void test6() {
    ClassA a = new ClassA();
    a.setId(16);
    a.setName("java16");
    ClassA b = new ClassA();
    b.setId(7);
    b.setName("java7");
    // 先实例化
    HashSet<ClassA> s = new HashSet();
    s.add(a);
    s.add(b);
    //得到迭代器
    Iterator i=s.iterator();
    //hasNext() 有没有下一次元素  布尔型
    while(i.hasNext()){
    //next()方法取出
        ClassA c=(ClassA)i.next();
        System.out.println(c.getName());
        System.out.println(c.getId());
    }
}

[如何边遍历集合边删除元素--使用Iterator中的remove()方法]

否则会引发ConcurrentModifactionException()

@Test
public void test1(){
    Set<String> s=new HashSet();
    s.add("a");
    s.add("c");
    s.add("b");
    Iterator i=s.iterator();
    while(i.hasNext()){
        String str=(String) i.next();
        //如果str为a就移除
        if("a".equals(str)){
            //错误写法s.remove(str);
            //正确
            i.remove();
        }
    }
    
    System.out.println(s);
}

课堂练习
使用Set集合,生成1-25之内不重复的7个随机整数

@Test
    public void test11() {
        HashSet<Integer> hs = new HashSet();
        for (;;) {
            int r = new Random().nextInt(25) + 1;
            hs.add(r);
            if (hs.size() == 7) {
                System.out.println(hs);
                break;
            }
        }
    }


    @Test
    // 使用Set集合,生成1-25之内不重复的7个随机整数
    public void try05() {
        HashSet<Integer> hs = new HashSet();
        int count = 0;
        while (count < 7) {
            Integer j = new Random().nextInt(25) + 1;
            if (hs.add(j)) {
                count++;
            }
        }
        for (Integer i : hs) {
            System.out.println(i);
        }
    }

public class test {
public static void main(String[] args)  {
HashSet<Integer> h=new HashSet<>();
while (h.size()<7){
h.add((int)(Math.random()*25+1));
}
System.out.println(h);
}
}

List

List:ArrayList(常用) LinkedList Vector(线程安全)
ArrayList:有序的 可重复 底层是基于数组实现的 随机读取时速度快 适合做查询
LinkedList:底层是基于链接实现的 可以用来做栈和队列,特点是:插入和删除快,查询慢
Vector:线程安全的 做线程同步,速度会慢

栈:先进后出(类似给枪压子弹)
队列:先进先出 queue

@Test
    public void test() {
  List<String> s=new ArrayList(); 
s.add("liucy");
        s.add("tanzy");
        s.add("liucy");
        s.add("zhanghs");
        s.add("shenp");
  for(String name:s){
      System.out.println(name);
  }
    }
    @Test
    public void test5() {
        ClassA a = new ClassA();
        a.setId(16);
        a.setName("java16");
        ClassA b = new ClassA();
        b.setId(7);
        b.setName("java7");
        // 先实例化
        List<ClassA> s = new ArrayList();
        s.add(a);
        s.add(b);
        for (ClassA c : s) {
            System.out.println(c.getId() + " " + c.getName());
        }
    }

ListIterator
先正向再反向读

@Test
    public void test2() {
  List<String> s=new ArrayList(); 
s.add("liucy");
        s.add("tanzy");
        s.add("liucy");
        s.add("zhanghs");
        s.add("shenp");
        //正向输出
  ListIterator i=s.listIterator();
  while(i.hasNext()){
      System.out.println(i.next());
  }
  System.out.println("------------------->");
  //反向输出
  while(i.hasPrevious()){
      System.out.println(i.previous());
  }
    }

上面的代码如果把正向的注释掉,反向的也读不出来,原因是指针在最上面,解决方式是可以在listIterator参数里把size大小加进去。让指针先跳转到指定位置

    @Test
    public void test3() {
    List<String> s=new ArrayList(); 
s.add("liucy");
        s.add("tanzy");
        s.add("liucy");
        s.add("zhanghs");
        s.add("shenp");
        //读取第3个元素
        String ss=s.get(3);
        System.out.println("第三个"+ss);
        //正向输出
  ListIterator i=s.listIterator(s.size());
  System.out.println("------------------->");
  //反向输出
  while(i.hasPrevious()){
      System.out.println(i.previous());
  }
    }

LinkedList常用操作

新增方法:addFirst() addLast() getFirst() getLast() removeFirst() removeLast()
addFirst() addLast()例子:

@Test
public void test() {
    LinkedList<String> s = new LinkedList();
    s.add("liucy");
    s.add("tanzy");
    s.add("liucy");
    s.add("zhanghs");
    s.add("shenp");
    // addFirst从顶面add
    s.addFirst("NUMER ONE");
    // addLast以底部add
    s.addLast("我是倒数第一");
    s.removeFirst();
    s.removeFirst();
    s.removeLast();
    for (String name : s) {
        System.out.println(name);
    }
}

Collections类

  @Test
public void test01() {
    // 1、求最大值
    List<Integer> list01 = new ArrayList();
    list01.add(20);
    list01.add(25);
    list01.add(19);
    list01.add(27);
    list01.add(28);
    // 作业:Collections(list,com)
    //Integer maxAge = Collections.max(list01);
    //System.out.println(maxAge);
    //Integer minAge = Collections.min(list01);
    //System.out.println(minAge);
    //reverse 28, 27, 19, 25, 20  只是把顺序进行了简单的反转
    //Collections.reverse(list01);
    //System.out.println("reverse之后的集合:"+list01);
    //sort从小到大排序
    //list01里的数据永远变动
    Collections.sort(list01);
    System.out.println("sort之后的集合:"+list01);
    //binarySearch 查询集合中是否存在指定的元素
    //19时-2  28-4
    //19-0 20 25-2 27 28-4
    //如果查找的对象存在,返回排序的对象索引 0

// 如果不存在,就返回负数
System.out.println(list01);
//在变化后的list01中进行操作
int result=Collections.binarySearch(list01, 23);
System.out.println("查找结果"+result);
//fill copy
// 用list02的值覆盖
Collections.copy(list01, list02);
// Collections.copy(list02, list01);
System.out.println("copy后list01-->" + list01);
System.out.println("copy后list02-->" + list02);

}

今天在学习集合中 有关对象的深度拷贝和浅拷贝 的时候,像拷贝一个ArrayList对象到另一个ArrayList对象中,正好发现Collections有一个copy方法。可是不好用啊总是报错。查看api才知道,它的capacity(容纳能力大小)可以指定(最好指定)。而初始化时size的大小永远默认为0,只有在进行add和remove等相关操作 时,size的大小才变化。然而进行copy()时候,首先做的是将desc的size和src的size大小进行比较,只有当desc的 size 大于或者等于src的size时才进行拷贝,否则抛出IndexOutOfBoundsException异常;

//想拷贝list01到list02

//复制了解即可
@Test
public void test02() {
    List<String> list01 = new ArrayList<String>();
    list01.add("111");
    list01.add("222");
    list01.add("333");
    list01.add("444");
    List<String> list02 = new ArrayList<String>();
    Collections.addAll(list02, new String[list01.size()]);
    Collections.copy(list02, list01);
    System.out.println(list02);
}


@Test
public void test02() {
    List<String> list01 = new ArrayList<String>();
    list01.add("111");
    list01.add("222");
    list01.add("333");
    list01.add("444");
    List<String> list02 = new ArrayList<String>();
    list02.addAll(list01);
    System.out.println(list01);
    System.out.println(list02);
    list01.add("555");
    System.out.println(""+list01);
    System.out.println(list02);
}

课堂练习:试验collections其他方法

作业:

1、请使用LinkedList来模拟一个队列(先进先出的特性): [必做题]
1.1 拥有放入对象的方法void put(Object o)
1.2 取出对象的方法Object get()
1.3 判断队列当中是否为空的方法boolean isEmpty();并且,编写测试代码,验证你的队列是否正确。

  2、产生10个1-100的随机数,并放到一个数组中,把数组中大于等于10的数字放到一个list集合中,并打印到控制台。
  3、双色球规则:双色球每注投注号码由6个红色球号码和1个蓝色球号码组成。红色球号码从1—33中选择;蓝色球号码从1—16中选择;请随机生成一注双色球号码。(要求同色号码不重复)

map

Map :key-value
map:HashMap基于哈希的 TreeMap基于红黑树的 LinkedHashMap

@Test
    public void test1() {
        ClassA a = new ClassA();
        a.setId(16);
        a.setName("java16");
        ClassA b = new ClassA();
        b.setId(7);
        b.setName("java7");
        // 新建一个map
        Map<String, ClassA> map = new HashMap();
        // 向map里存放数据put方法
        map.put("c7", b);
        map.put("c16", a);
        // 单个读取
        // 我知道key的情况下,根据key去查询。用get方法
        // 比如我想读取编号为c16的班级的信息
        ClassA c16 = map.get("c16");
        System.out.println(c16.getId() + c16.getName());
        //如果get里的key不存在,就会抛异常NullPointer空指针异常
        //所以在根据key读取时,最好先判断有没有
    if(map!=null&&!map.isEmpty()){
        if(map.containsKey("c17")){
        ClassA c17 = map.get("c17");
        System.out.println(c17.getName());
        }else{
            System.out.println("不存在这个key的数据");
        }
    }
    }

再来一个例子 模拟存储最后位置

    public class Point {
        //纬度41.8
        private double lat;
        //经度123.4
        private double lon;
        public Point(double lat, double lon) {
            super();
            this.lat = lat;
            this.lon = lon;
        }
    
        public double getLat() {
            return lat;
        }
    
        public void setLat(double lat) {
            this.lat = lat;
        }
    
        public double getLon() {
            return lon;
        }
    
        public void setLon(double lon) {
            this.lon = lon;
        }
    
    }

// 1)创建:构造方法指定初始化个数或不指定初始化次数 new HashMap()--默认是初始化10 new HashMap(8) --掌握
// 2)添加: 单个添加put(key,value) --掌握
// 批量添加:putAll(Map t)
// 3)修改: 单个put(key,value) 批量:putAll(Map t) 相同key的覆盖原有的。

// 4)删除: clear() --删除所有
// remove(key) --删除指定key的一条数据
// 5)查询单个:get(key) 利用key取出value --掌握
// containsKey(key)是否存在指定key --掌握
// containsValue(value) 是否存在指定value ---了解
// 查询所有:size()-一共几条
// isEmpty()是否为空
// keyset-有哪些key ---难 了解
// entryset-(key,value) ---难 了解
@Test
public void test03() {
    // 根据手机号码,查询最后位置
    // step1 建立一个point实体
    // 沈阳的经纬度lon= 123.4 lat=41.8
    // step2 建一个map用于存放手机号及该手机号的最后位置
    // map<手机号,最后位置>
    Map<String, Point> lastPosMap = new HashMap();
    // step3 向map里放每个人的最后位置
    Point p1 = new Point(41.8, 123.4);
    lastPosMap.put("13904001139", p1);
    // 一口气写法
    lastPosMap.put("13840191050", new Point(40.9, 123.42));// 王哥
    lastPosMap.put("13840191051", new Point(41.9, 123.445));
    // 假设这个人动了,就再上报一次位置
    lastPosMap.put("13840191050", new Point(40.92, 123.423));// 王哥 131行的被覆盖
    // 查询指定手机号的最后位置
    // 能不能孙宇的最后位置
    if (lastPosMap.containsKey("18695876927")) {// 是否存在18695876927的最后位置信息呢
        System.out.println("有孙宇的最后位置");
    } else {
        System.out.println("对不起,没查到");
    }
    if (lastPosMap.containsKey("13840191050")) {// 是否存在18695876927的最后位置信息呢
        // 如果可以查到,我就可以通过get(key)方法把王浩丞的最后位置拿到。在地图上描画
        Point wanglast = lastPosMap.get("13840191050");
        // 打印一下位置里的详细信息
        System.out.println("王浩丞的位置信息" + wanglast.getLat() + " " + wanglast.getLon());
    }
    // 上面的方法比较常用 必须熟练掌握的
    // 接下来的方法 练习

}

// 以下这些不常用 了解
@Test
public void test04() {
    // 4)删除: clear() --删除所有--比较常用 delete 不带条件
    Map<String, Point> lastPosMap = new HashMap();
    // step3 向map里放每个人的最后位置
    Point p1 = new Point(41.8, 123.4);
    lastPosMap.put("13904001139", p1);
    // 一口气写法
    lastPosMap.put("13840191050", new Point(40.9, 123.42));// 王哥
    lastPosMap.put("13840191051", new Point(41.9, 123.445));
    //lastPosMap.clear();
    //System.out.println("删除数据后的map"+lastPosMap);
    // remove(key) --删除指定key的一条数据 --比较常用
    //lastPosMap.remove("13904001139");//delete from 表名 where key=?
    //System.out.println("删除数据后的map"+lastPosMap);
    // 5)查询单个:
    // containsValue(value) 是否存在指定value ---了解即可    不常用
    //Point p1 = new Point(41.8, 123.4);
    //lastPosMap.put("13904001139", p1);
    Point p4 = new Point(41.8, 123.4);
    lastPosMap.put("13904001139", p4);
    if(lastPosMap.containsValue(p1)){//true/false
        System.out.println("存在");
    }else{
        System.out.println("不存在");
    }
    // 查询所有:size()-一共几条--比较常用
    System.out.println("map里有几条数据:"+lastPosMap.size());
    // isEmpty()是否为空
    if(lastPosMap.isEmpty()){
        System.out.println("为空");
    }else{
        System.out.println("不为空");
    }
}

map循环

第一种:循环keySet,然后通过key读取value

  @Test
public void test05() {
    // keyset-有哪些key ---难了解
    // entryset-(key,value) ---难    优先掌握
    Map<String, Point> lastPosMap = new HashMap();
    // step3 向map里放每个人的最后位置
    Point p1 = new Point(41.8, 123.4);
    lastPosMap.put("13904001139", p1);
    // 一口气写法
    lastPosMap.put("13840191050", new Point(40.9, 123.42));// 王哥
    lastPosMap.put("13840191051", new Point(41.9, 123.445));
    //查询所有,有两种方式keyset
    //step1 
    Set<String>  keys=lastPosMap.keySet();//所在key的集合
    //用加强for,循环到每个key
    for(String name:keys){
        System.out.println(name);
        //step2 用key获取value
        Point p=lastPosMap.get(name);
        System.out.println(p.getLat()+" "+p.getLon());
    }
}
// entryset-(key,value) ---难    优先掌握
@Test
public void test06() {
    // keyset-有哪些key ---难了解
    // entryset-(key,value) ---难    优先掌握
    Map<String, Point> lastPosMap = new HashMap();
    // step3 向map里放每个人的最后位置
    Point p1 = new Point(41.8, 123.4);
    lastPosMap.put("13904001139", p1);
    // 一口气写法
    lastPosMap.put("13840191050", new Point(40.9, 123.42));// 王哥
    lastPosMap.put("13840191051", new Point(41.9, 123.445));
    //通过循环entryset来获取entry(key,value)
    Set<Entry<String,Point>> s=lastPosMap.entrySet();
    //循环entry实体,就可以拿到key value
    for(Entry<String,Point> p:s){
        String phone=p.getKey();
        Point lp=p.getValue();
        System.out.println(lp.getLat()+" " +lp.getLon());
    }
}

treeMap key顺序 字母a-Z 数学按大学排序

@Test
public void test08() {
    Map<Integer, Point> lastPosMap = new TreeMap();
    // step3 向map里放每个人的最后位置
    Point p1 = new Point(41.8, 123.4);
    lastPosMap.put(140, p1);
    lastPosMap.put(139, new Point(41.9, 123.445));
    // 一口气写法
    lastPosMap.put(150, new Point(40.9, 123.42));// 王哥
    
    //通过循环entryset来获取entry(key,value)
    Set<Entry<Integer,Point>> s=lastPosMap.entrySet();
    //循环entry实体,就可以拿到key value
    for(Entry<Integer,Point> p:s){
        Integer phone=p.getKey();
        Point lp=p.getValue();
        System.out.println(phone+" "+lp.getLat()+" " +lp.getLon());
    }
}

作业:

    /**
    * 1、请使用LinkedList来模拟一个队列(先进先出的特性): [必做题] 
    * 1.1 拥有放入对象的方法void put(Object o) 
    * 1.2 取出对象的方法Object get() 
    * 1.3 判断队列当中是否为空的方法boolean isEmpty();
    * 并且,编写测试代码,验证你的队列是否正确。
    */
    public class MyQueue {
        //类级别定义一个LinkedList类型的变量
        LinkedList queue=new LinkedList();
        
        public void put(Object o){
            queue.addFirst(o);
        }
        public Object get(){
            Object obj=queue.getLast();
            queue.removeLast();
            return obj;
        }
        
        public boolean isEmpty(){
            return queue.isEmpty();
        }
        
        public static void main(String[] args) {
            MyQueue mq=new MyQueue();
            mq.put("hello1");
            mq.put("hello2");
            mq.put("hello3");
            
            while(!mq.isEmpty()){
                Object o=mq.get();
                System.out.println(o);
            }
        }
        
    }

2、产生10个1-100的随机数,并放到一个数组中, 把数组中大于等于10的数字放到一个list集合中,并打印到控制台
3、双色球规则:双色球每注投注号码由6个红色球号码和1个蓝色球号码组成。
红色球号码从1―33中选择;蓝色球号码从1―16中选择;请随机生成一注双色球号码。
(要求同色号码不重复)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容