传智播客-Java学习笔记day17

1.(ArrayList集合的toString()方法源码解析
解析
代码:
    Collection c = new ArrayList();
    c.add("hello");
    c.add("world");
    c.add("java");
 
    System.out.println(c);
 
为什么c输出的不是地址值呢?
    A:Collection c = new ArrayList();
        这是多态,所以输出c的toString()方法,其实是输出ArrayList的toString()
    B:看ArrayList的toString()
        而我们在ArrayList里面却没有发现toString()。
        以后遇到这种情况,也不要担心,你认为有,它却没有,就应该去它父亲里面看看。
    C:toString()的方法源码
 
        public String toString() {
            Iterator<E> it = iterator(); //集合本身调用迭代器方法,得到集合迭代器
            if (! it.hasNext())
                return "[]";
 
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            for (;;) {
                E e = it.next(); //e=hello,world,java
                sb.append(e == this ? "(this Collection)" : e);
                if (! it.hasNext())
                    //[hello, world, java]
                    return sb.append(']').toString();
                sb.append(',').append(' ');
            }
        }
 
2.用户登录注册案例
 
part1:分析图解
part2:用户登录注册案例分析
需求:用户登录注册案例。
 
按照如下的操作,可以让我们更符号面向对象思想
    A:有哪些类呢?
    B:每个类有哪些东西呢?
    C:类与类之间的关系是什么呢?
 
分析:
    A:有哪些类呢?
        用户类
        测试类
    B:每个类有哪些东西呢?
        用户类:
            成员变量:用户名,密码
            构造方法:无参构造
            成员方法:getXxx()/setXxx()
                       登录,注册
 
             假如用户类的内容比较对,将来维护起来就比较麻烦,为了更清晰的分类,我们就把用户又划分成了两类
                用户基本描述类
                    成员变量:用户名,密码
                    构造方法:无参构造
                    成员方法:getXxx()/setXxx()
                用户操作类
                    登录,注册
        测试类:
            main方法。
    C:类与类之间的关系是什么呢?
        在测试类中创建用户操作类和用户基本描述类的对象,并使用其功能。
 
分包:
    A:功能划分
    B:模块划分
    C:先按模块划分,再按功能划分
 
今天我们选择按照功能划分:
    用户基本描述类包 cn.itcast. pojo
    用户操作接口 cn.itcast. dao
    用户操作类包 cn.itcast .dao.impl
         今天是集合实现,过几天是IO实现,再过几天是GUI实现,就业班我们就是数据库实现
    用户测试类 cn.itcast.test
 
part3:用户登录注册案例用户 基本描述类用户操作接口的实现
  新建一个包  cn.itcast.pojo
=============分割线==============
package cn.itcast.pojo;
 
/**
 * 这是用户基本描述类
 * 
 * @author 风清扬
 * @version V1.0
 * 
 */
public class User {
    // 用户名
    private String username;
    // 密码
    private String password;
 
    public User() {
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
}
 
============分割线==================
再新建一个包 cn.itcast.dao
 
========分割线===============
package cn.itcast.dao;
 
import cn.itcast.pojo.User;
 
/**
 *  这是针对用户进行操作的接口
 * 
 * @author 风清扬
 * @version V1.0
 * 
 */
public interface UserDao {
    /**
     * 这是用户登录功能
     * 
     * @param username
     *            用户名
     * @param password
     *            密码
     * @return 返回登录是否成功
     */
    public abstract boolean isLogin(String username, String password);
 
    /**
     * 这是用户注册功能
     * 
     * @param user
     *            要注册的用户信息
     */
    public abstract void regist(User user);
}
========分割线==========================
两个注意问题
 
再新建一个包 cn.itcast.dao.impl  
UserDaoImpl.java
============分割线=================
package cn.itcast.dao.impl;
 
import java.util.ArrayList;
 
import cn.itcast.dao.UserDao;
import cn.itcast.pojo.User;
 
/**
 * 这是用户操作的具体实现类(集合版)
 * 
 * @author 风清扬
 * @version V1.0
 * 
 */
public class UserDaoImpl implements UserDao {
     // 为了让多个方法能够使用同一个集合,就把集合定义为成员变量
    // 为了不让外人看到,用private
    // 为了让多个对象共享同一个成员变量,用static
    private static ArrayList<User> array = new ArrayList<User>();
 
    @Override
    public boolean isLogin(String username, String password) {
        // 遍历集合,获取每一个用户,并判断该用户的用户名和密码是否和传递过来的匹配
        boolean flag = false;
 
        for (User u : array) {
            if (u.getUsername().equals(username)
                    && u.getPassword().equals(password)) {
                flag = true;
                break;
            }
        }
 
        return flag;
    }
 
    @Override
    public void regist(User user) {
        // 把用户信息存储集合
        // ArrayList<User> array = new ArrayList<User>();
        array.add(user);
    }
}
 
===========分割线================
 
再新建一个包 cn.itcast.test
UserTest.java

 

==========分割线====================
package cn.itcast.test;
 
import java.util.Scanner;
 
import cn.itcast.dao.UserDao;
import cn.itcast.dao.impl.UserDaoImpl;
import cn.itcast.game.GuessNumber;
import cn.itcast.pojo.User;
 
/**
 * 用户测试类
 * 
 * @author 风清扬
 * @version V1.0
 * 
 *           新增加了两个小问题 A:多个对象共享同一个成员变量,用静态
 *          B:循环里面如果有switch,并且在switch里面有break,那么结束的不是循环,而是switch语句
 * 
 */
public class UserTest {
    public static void main(String[] args) {
        // 为了能够回来
        while (true) {
            // 欢迎界面,给出选择项
            System.out.println("--------------欢迎光临--------------");
            System.out.println("1 登录");
            System.out.println("2 注册");
            System.out.println("3 退出");
            System.out.println("请输入你的选择:");
            // 键盘录入选择,根据选择做不同的操作
            Scanner sc = new Scanner(System.in);
            // 为了后面的录入信息的方便,我所有的数据录入全部用字符接收
            String choiceString = sc.nextLine();
 
            // switch语句的多个地方要使用,我就定义到外面
             UserDao ud = new UserDaoImpl();
 
            // 经过简单的思考,我选择了switch,而且是 JDK1.7以及以后才能用,因为switch接收了字符串
             switch (choiceString) {
            case "1":
                // 登录界面,请输入用户名和密码
                System.out.println("--------------登录界面--------------");
                System.out.println("请输入用户名:");
                String username = sc.nextLine();
                System.out.println("请输入密码:");
                String password = sc.nextLine();
 
                // 调用登录功能
                // UserDao ud = new UserDaomImpl();
 
                 boolean flag = ud.isLogin(username, password);
                if (flag) {
                    System.out.println("登录成功,可以开始玩游戏了");
 
                    System.out.println("你玩吗?y/n");
                    while (true) {
                        String resultString = sc.nextLine();
                        if (resultString.equalsIgnoreCase("y")) {
                            // 玩游戏
                            GuessNumber.start();
                            System.out.println("你还玩吗?y/n");
                        } else {
                            break;
                        }
                    }
                    System.out.println("谢谢使用,欢迎下次再来");
                     System.exit(0);
                     // break; //这里写break,结束的是switch
                } else {
                    System.out.println("用户名或者密码有误,登录失败");
                }
                break;
            case "2":
                // 欢迎界面,请输入用户名和密码
                System.out.println("--------------注册界面--------------");
                System.out.println("请输入用户名:");
                String newUsername = sc.nextLine();
                System.out.println("请输入密码:");
                String newPassword = sc.nextLine();
 
                // 把用户名和密码封装到一个对象中
                 User user = new User();
                user.setUsername(newUsername);
                user.setPassword(newPassword);
 
                // 调用注册功能
                // 多态
                // UserDao ud = new UserDaoImpl();
                // 具体类使用
                // UserDaoImpl udi = new UserDaoImpl();
 
                ud.regist(user);
                System.out.println("注册成功");
                break;
            case "3":
            default:
                System.out.println("谢谢使用,欢迎下次再来");
                 System.exit(0);
                //break;
            }
        }
    }
}
===============分割线===================
3.Set集合概述及特点
Collection
 *         |--List
 *             有序(存储顺序和取出顺序一致),可重复
 *         |--Set
 *              无序(存储顺序和取出顺序不一致), 唯一
 * 
 * HashSet:它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。
 * 注意:虽然Set集合的元素无序,但是,作为集合来说,它肯定有它自己的存储顺序,
 * 而你的顺序恰好和它的存储顺序一致,这代表不了有序,你可以多存储一些数据,就能看到效果。
 
4.HashSet集合的add()方法的 源码以及解析
 HashSet:存储字符串并遍历
 * 问题:为什么 存储字符串的时候, 字符串内容相同的只存储了一个呢?
 
interface Collection {
    ...
}
 
interface Set extends Collection {
    ...
}
 
class HashSet implements Set {
    private static final Object PRESENT = new Object();
    private transient HashMap<E,Object> map;
 
    public HashSet() {
        map = new HashMap<>();
    }
 
    public boolean add(E e) { //e=hello,world
        return map.put(e, PRESENT)==null;
    }
}
 
class HashMap implements Map {
    public V put(K key, V value) { //key=e=hello,world
 
        //看哈希表是否为空,如果空,就开辟空间
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
 
        //判断对象是否为null
        if (key == null)
            return putForNullKey(value);
 
         int hash = hash(key); //和对象的hashCode()方法相关
 
        //在哈希表中查找hash值
        int i = indexFor(hash, table.length);
         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            //这次的e其实是第一次的world
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
                //走这里其实是没有添加元素
            }
        }
 
        modCount++;
        addEntry(hash, key, value, i); //把元素添加
        return null;
    }
 
    transient int hashSeed = 0;
 
    final int hash(Object k) { //k=key=e=hello,
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
 
        h ^= k.hashCode(); //这里调用的是对象的hashCode()方法
 
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
}
 
 
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
 
总结:通过查看add方法的源码,我们知道这个方法底层依赖 两个方法:hashCode()和equals()。
 * 步骤:
 *         首先比较哈希值
 *         如果相同,继续走,比较地址值或者走equals()
 *         如果不同,就直接添加到集合中    
 * 按照方法的步骤来说:    
 *         先看hashCode()值是否相同
 *             相同:继续走equals()方法
 *                 返回true:    说明元素重复,就不添加
 *                 返回false:说明元素不重复,就添加到集合
 *             不同:就直接把元素添加到集合
 *  如果类没有重写这两个方法,默认使用的Object()。一般来说不会相同。
 *  而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
 
以下两个重写都在学生类Student类中修改
重写hashCode方法

      重写equals方法

需要手动重写的代码如下
 @Override
         public int hashCode() {
         // return 0;
         //  因为成员变量值影响了哈希值,所以我们把成员变量值相加即可
         // return this.name.hashCode() + this.age;
         // 看下面
         // s1:name.hashCode()=40,age=30
         // s2:name.hashCode()=20,age=50
         // 尽可能的 区分,我们可以把它们乘以一些整数(乘上整数 扩大分差把细微差别放大避免偶尔误差)
         return this.name.hashCode()  + this.age  * 15;
         }
 
         @Override
         public boolean equals(Object obj) {
         //  System.out.println(this + "---" + obj);//测试比较顺序以及比较次数的代码,测试后注释掉
         if (this == obj) {
         return true;
         }
 
         if (!(obj  instanceof Student)) {
         return false;
         }
 
         Student s = (Student) obj;
         return this.name.equals(s.name) && this.age == s.age;
         }
@Override
         public String toString() {
         return "Student [name=" + name + ", age=" + age + "]";
         }
优化测试截图(优化前hashCode默认返回值是0.也就是所有的元素哈希值都一样)
优化前,比较次数比较大

 优化后(哈希值不再一样,通过哈希值与成员变量相加来重写hashCode方法避免不必要的比较)

但实际上,一般不自己想写这么麻烦,可以用代码自动生成功能来实现

 

 

以后解决HashSet 存储自定义对象时元素不唯一问题就可以用重写hashCode()方法和equals()方法解决

 

5.HashSet集合存储自定义对象并遍历
如果对象的成员变量值相同即为同一个对象
 
注意了:
 *         你使用的是HashSet集合,这个集合的底层是哈希表结构。
 *         而哈希表结构底层依赖:hashCode()和equals()方法。
 *         如果你认为对象的成员变量值相同即为同一个对象的话,你就应该重写这两个方法。
 *         如何重写呢?不同担心,自动生成即可。
 
import java.util.HashSet;
 
public class DogDemo {
    public static void main(String[] args) {
        HashSet<Dog> hs = new HashSet<Dog>();
 
        Dog d1 = new Dog("网", 27, "绿色", '男');
        Dog d2 = new Dog("随", 27, "绿色", '男');
        Dog d3 = new Dog("王", 27, "绿色", '男');
        Dog d4 = new Dog("网", 27, "绿色", '男');
        Dog d5 = new Dog("网", 27, "绿色", '女');
 
        hs.add(d1);
        hs.add(d2);
        hs.add(d3);
        hs.add(d4);
        hs.add(d5);
 
        for (Dog d : hs) {
            System.out.println(d.getName() + "---" + d.getAge() + "---" + d.getColor() + "---" + d.getSex());
        }
 
    }
}
 
Dog类
public class Dog {
    private String name;
    private int age;
    private String color;
    private char sex;
 
    public Dog() {
        super();
        // TODO Auto-generated constructor stub
    }
 
    public Dog(String name, int age, String color, char sex) {
        super();
        this.name = name;
        this.age = age;
        this.color = color;
        this.sex = sex;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public String getColor() {
        return color;
    }
 
    public void setColor(String color) {
        this.color = color;
    }
 
    public char getSex() {
        return sex;
    }
 
    public void setSex(char sex) {
        this.sex = sex;
    }
 
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((color == null) ? 0 : color.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + sex;
        return result;
    }
 
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Dog other = (Dog) obj;
        if (age != other.age)
            return false;
        if (color == null) {
            if (other.color != null)
                return false;
        } else if (!color.equals(other.color))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (sex != other.sex)
            return false;
        return true;
    }
 
}
6.TreeSet
TreeSet:能够对元素按照某种规则进行排序。
排序有两种方式
 * A:自然排序
 * B:比较器排序
 * 
 * TreeSet集合的特点:排序和唯一
 * 
 * 通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法。
 
7. TreeSet保证元素排序的源码解析
interface Collection {...}
 
interface Set extends Collection {...}
 
interface NavigableMap {
 
}
 
class TreeMap implements NavigableMap {
     public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check
 
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
         Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
             Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k. compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
}
 
class TreeSet implements Set {
    private transient NavigableMap<E,Object> m;
 
    public TreeSet() {
         this(new TreeMap<E,Object>());
    }
 
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
}
 
真正的比较是 依赖于元素的compareTo()方法,而这个方法是 定义在 Comparable里面的。
所以,你要想重写该方法,就必须是先  实现Comparable接口。这个接口表示的就是 自然排序
 
附:TreeSet保证元素唯一性和自然排序的原理和图解
7.TreeSet存储自定义对象并遍历练习1
 
下面贴代码
TreeSetDemo2.java
===============分割线===================
import java.util.TreeSet;
 
/*
 * TreeSet存储自定义对象并保证排序和唯一。
 * 
 * A:你没有告诉我们怎么排序
 *         自然排序,按照年龄从小到大排序
 * B:元素什么情况算唯一你也没告诉我
 *         成员变量值都相同即为同一个元素
 */
public class TreeSetDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();
 
        // 创建元素
        Student s1 = new Student("linqingxia", 27);
        Student s2 = new Student("zhangguorong", 29);
        Student s3 = new Student("wanglihong", 23);
        Student s4 = new Student("linqingxia", 27);
        Student s5 = new Student("liushishi", 22);
        Student s6 = new Student("wuqilong", 40);
        Student s7 = new Student("fengqingy", 22);
 
        // 添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
 
        // 遍历
        for (Student s : ts) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}
==============分割线==============
Student类
Student.java
==============分割线==============
 
/*
 *  如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
 */
public class Student  implements Comparable<Student> {
    private String name;
    private int age;
 
    public Student() {
        super();
    }
 
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    @Override
    public int compareTo(Student s) {
        // return 0;
        // return 1;
        // return -1;
 
        // 这里返回什么,其实应该根据我的排序规则来做
        // 按照年龄排序,主要条件
        int num = this.age - s.age;
        // 次要条件
        //  年龄相同的时候,还得去看姓名是否也相同
        // 如果年龄和姓名都相同,才是同一个元素
        //String 默认已经实现了Comparable接口,所以可以大胆用compareTo方法
        int num2 = num == 0 ? this.name. compareTo(s.name) : num;
        return num2;
    }
}
 
下面是以上代码的解析。
 
附:A:首先解析以下这行代码
     int num2 = num == 0 ?  this.name. compareTo( s.name) : num;
解:这行语句是三目运算符,意思是先比较主要条件---年龄,num为年龄比较产生的返回值,如果是零就不插入二叉树(TreeSet)中(通过比较发现年龄重复就不添加),如果此时直接返回num的话会造成Student对象的意外丢失(例如年龄相同但实际上姓名不相同的学生对象),
 
因此为了解决这个问题,新添加了num2,考虑多了次要条件姓名,如果年龄相同的同时姓名也相同就返回num2的值0,代表不添加元素到二叉树中。
 
B:String 默认已经实现了Comparable接口
 
C:为什么要在自定义类Student中实现Comparable接口呢?(implements Comparable<T>)
如果不实现这个接口,运行如下
 
查API
 
由上图可知:实现这个借口 是为了进行自然排序。Student类默认并没有实现Comparable接口
 
Comparable接口 只有一个compareTo方法,实现接口时需要重写这个方法
 
8.TreeSet存储自定义对象并遍历练习2
先贴代码
==============分割线=======================
import java.util.TreeSet;
 
/*
 * 需求:请按照姓名的长度排序
 */
public class TreeSetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();
 
        // 创建元素
         Student s1 = new Student("linqingxia", 27);
        Student s2 = new Student("zhangguorong", 29);
         Student s3 = new Student("wanglihong", 23);//s1与s3名字长度相同
         Student s4 = new Student("linqingxia", 27);
        Student s5 = new Student("liushishi", 22);
        Student s6 = new Student("wuqilong", 40);
        Student s7 = new Student("fengqingy", 22);
         Student s8 = new Student("linqingxia", 29);
 
        // 添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
        ts.add(s8);
 
        // 遍历
        for (Student s : ts) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}
 
=================分割线=====================
Student类
 
/*
 *  如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
 */
public class Student implements Comparable<Student> {
    private String name;
    private int age;
 
    public Student() {
        super();
    }
 
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
     @Override
    public int compareTo(Student s) {
        // 主要条件 姓名的长度
        int num = this.name.length() - s.name.length();
        // 姓名的长度相同,不代表姓名的内容相同
        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        // 姓名的长度和内容相同,不代表年龄相同,所以还得继续判断年龄
        int num3 = num2 == 0 ? this.age - s.age : num2;
        return num3;
    }
}
 
留意看上面stuedent类中的compareTo方法,虽然需求中只说明了按照姓名的长度排序,但是 隐含的比较,隐含的条件(如姓名的内容,年龄是否相同)却是不能忽略的!
 
9.TreeSet保证元素唯一性和 比较器排序的原理及代码实现
 
TreeSet集合保证元素排序和唯一性的原理
 *  唯一性:是根据比较的返回是否是0来决定。
 * 排序:
 *         A:自然排序(元素具备比较性)
 *             让元素所属的类实现自然排序接口 Comparable
 *         B: 比较器排序(集合具备比较性)
 *             让集合的构造方法接收一个 比较器接口的子类对象 Comparator
 
查API-----TreeSet  比较器排序  带参构造方法

 

 

首先,惯例新建一个TreeSetDemo
=========分割线==========
import java.util.Comparator;
import java.util.TreeSet;
 
/*
 * 需求:请按照姓名的长度排序
 */
public class TreeSetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        // TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
         // public TreeSet(Comparator comparator) //比较器排序
         TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
 
        // 创建元素
        Student s1 = new Student("linqingxia", 27);
        Student s2 = new Student("zhangguorong", 29);
        Student s3 = new Student("wanglihong", 23);
        Student s4 = new Student("linqingxia", 27);
        Student s5 = new Student("liushishi", 22);
        Student s6 = new Student("wuqilong", 40);
        Student s7 = new Student("fengqingy", 22);
        Student s8 = new Student("linqingxia", 29);
 
        // 添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
        ts.add(s8);
 
        // 遍历
        for (Student s : ts) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}
===========分割线===============
在同一个包下定义一个Student类
================分割线======================
public class Student {
    private String name;
    private int age;
 
    public Student() {
        super();
    }
 
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
}
 
================分割线================
 
最后,同一个包下定义一个MyComparator类

 

 

import java.util.Comparator;
 
public class MyComparator  implements Comparator<Student> {
 
    @Override
    public int  compare(Student s1, Student s2) {
        // int num = this.name.length() - s.name.length();
        // this -- s1
        // s -- s2
        // 姓名长度
        int num =  s1.getName().length() - s2. getName().length();
        // 姓名内容
        int num2 = num == 0 ? s1. getName().compareTo(s2. getName()) : num;
        // 年龄
        int num3 = num2 == 0 ? s1. getAge() - s2. getAge() : num2;
        return num3;
    }
 
}
 
以下为一些解析说明
A:上面专门定义了一个类MyComparator,其实也可以省略这一个类,直接在TreeSetDemo测试类中定义
一个匿名内部类
如下
TreeSetDemo.java
=================分割线========================
import java.util.Comparator;
import java.util.TreeSet;
 
 
public class TreeSetDemo {
    public static void main(String[] args) {
        // 创建集合对象
        // TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
        // public TreeSet( Comparator comparator) //比较器排序
        // TreeSet<Student> ts = new TreeSet<Student>( new MyComparator());
 
        / / 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
        // 而匿名内部类就可以实现这个东西
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // 姓名长度
                int num = s1.getName().length() - s2.getName().length();
                // 姓名内容
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
                        : num;
                // 年龄
                int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                return num3;
            }
        });
 
        // 创建元素
        Student s1 = new Student("linqingxia", 27);
        Student s2 = new Student("zhangguorong", 29);
        Student s3 = new Student("wanglihong", 23);
        Student s4 = new Student("linqingxia", 27);
        Student s5 = new Student("liushishi", 22);
        Student s6 = new Student("wuqilong", 40);
        Student s7 = new Student("fengqingy", 22);
        Student s8 = new Student("linqingxia", 29);
 
        // 添加元素
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
        ts.add(s8);
 
        // 遍历
        for (Student s : ts) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}
 
===============分割线=======================
B:在MyComparator类 中getName(),getAge()与this.name 和this.age 问题
 
外部类不能访问private修饰的成员变量
C:注意两个不同的接口以及它们的方法
 
接口 Comparable======方法compareTo
接口 Comparator ==== 方法compare
* TreeSet集合保证元素排序和唯一性的原理
 * 唯一性:是根据比较的 返回是否是0来决定。
 * 排序:
 *         A: 自然排序( 元素具备比较性)(Student类实现比较接口)
 *             让元素所属的类实现自然排序接口 Comparable
 *         B: 比较器排序( 集合具备比较性)(也就是说,Student类不需要实现任何接口)
 *             让集合的构造方法接收一个比较器接口的子类对象 Comparator
 
 
10.一个案例----编写一个程序,获取 10个1至20的随机数,要求 随机数不能重复
 
import java.util.HashSet;
import java.util.Random;
 
/*
 * 编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
 * 
 * 分析:
 *         A:创建随机数对象
 *         B:创建一个HashSet集合
 *         C:判断集合的长度是不是小于10
 *             是:就创建一个随机数添加
 *             否:不搭理它
 *         D:遍历HashSet集合
 */
public class HashSetDemo {
    public static void main(String[] args) {
        // 创建随机数对象
         Random r = new Random();
 
        // 创建一个Set集合
        HashSet<Integer> ts = new HashSet<Integer>();//Integer默认已经实现了比较接口 Comparable
 
        // 判断集合的长度是不是小于10
        while (ts.size() < 10) {
            int num = r.nextInt(20) + 1;
            ts.add(num);
        }
 
        // 遍历Set集合
        for (Integer i : ts) {
            System.out.println(i);
        }
    }
}
 
 
11.一个案例----键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
TreeSetDemo .java
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
 
/*
 * 键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
 * 
 * 分析:
 *         A:定义学生类
 *         B:创建一个TreeSet集合
 *         C:总分从高到底如何实现呢?        
 *         D:键盘录入5个学生信息
 *         E:遍历TreeSet集合
 */
public class TreeSetDemo {
    public static void main(String[] args) {
        // 创建一个TreeSet集合
        TreeSet<Student> ts = new TreeSet<Student> (new Comparator<Student>() {
            @Override
            public int  compare(Student s1, Student s2) {
                 // 总分从高到低
                int num = s 2.getSum() -  s1.getSum();//s1与s2的位置已互换,因为成绩从高到低而不是默认的从低到高
                // 总分相同的不一定语文相同
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                // 总分相同的不一定数序相同
                int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
                // 总分相同的不一定英语相同
                int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
                // 姓名还不一定相同呢
                int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
                        : num4;
                return num5;
            }
        });
 
        System.out.println("学生信息录入开始");
        // 键盘录入5个学生信息
        for (int x = 1; x <= 5; x++) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入第" + x + "个学生的姓名:");
            String name = sc.nextLine();
            System.out.println("请输入第" + x + "个学生的语文成绩:");
            String chineseString = sc.nextLine();
            System.out.println("请输入第" + x + "个学生的数学成绩:");
            String mathString = sc.nextLine();
            System.out.println("请输入第" + x + "个学生的英语成绩:");
            String englishString = sc.nextLine();
 
            // 把数据封装到学生对象中
            Student s = new Student();
            s. setName(name);
            s .setChinese (Integer.parseInt(chineseString));
            s. setMath(Integer. parseInt(mathString));
            s. setEnglish(Integer. parseInt(englishString));
 
            // 把学生对象添加到集合
             ts.add(s);
        }
        System.out.println("学生信息录入完毕");
 
        System.out.println("学习信息从高到低排序如下:");
        System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
        // 遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() +  "\t" + s.getChinese() +  "\t"
                    + s.getMath() +  "\t" + s.getEnglish());
        }
    }
}
 
===================================================
Student类
 
public class Student {
    // 姓名
    private String name;
    // 语文成绩
    private int chinese;
    // 数学成绩
    private int math;
    // 英语成绩
    private int english;
 
    public Student(String name, int chinese, int math, int english) {
        super();
        this.name = name;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
    }
 
    public Student() {
        super();
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getChinese() {
        return chinese;
    }
 
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
 
    public int getMath() {
        return math;
    }
 
    public void setMath(int math) {
        this.math = math;
    }
 
    public int getEnglish() {
        return english;
    }
 
    public void setEnglish(int english) {
        this.english = english;
    }
 
    public int  getSum() {
        return  this.chinese + this.math + this.english;
    }
}
======================================
附上解析:
 int num = s 2.getSum() -  s1.getSum();
但是之前是这样的
 int num = s 1.getSum() -  s2.getSum();
运行如下

 

成绩并没有从高到低,所以
 int num = s 2.getSum() -  s1.getSum();
再次运行

 

12.最后附上一个致命bug 关于eclipse:

eclispe 控制台光标位置bug
Eclipse控制台输入数据后光标直接回到行首不跳到下一行

留意光标位置,要手动移动至下一行

 
不手动移到下一行会有莫明奇妙的异常
 
day17笔记补充
A:Collection集合总结(掌握)
    Collection
        |--List    有序,可重复
            |--ArrayList
                底层数据结构是数组,查询快,增删慢。
                线程不安全,效率高
            |--Vector
                底层数据结构是数组,查询快,增删慢。
                线程安全,效率低
            |--LinkedList
                底层数据结构是链表,查询慢,增删快。
                线程不安全,效率高
        |--Set    无序,唯一
            |--HashSet
                底层数据结构是哈希表。
                如何保证元素唯一性的呢?
                    依赖两个方法:hashCode()和equals()
                    开发中自动生成这两个方法即可
                |--LinkedHashSet
                    底层数据结构是链表和哈希表
                    由链表保证元素有序
                    由哈希表保证元素唯一
            |--TreeSet
                底层数据结构是红黑树。
                如何保证元素排序的呢?
                    自然排序
                    比较器排序
                如何保证元素唯一性的呢?
                    根据比较的返回值是否是0来决定
 
B:针对Collection集合我们到底使用谁呢?(掌握)
    唯一吗?
        是:Set
            排序吗?
                是:TreeSet
                否:HashSet
        如果你知道是Set,但是不知道是哪个Set,就用HashSet。
 
        否:List
            要安全吗?
                是:Vector
                否:ArrayList或者LinkedList
                    查询多:ArrayList
                    增删多:LinkedList
        如果你知道是List,但是不知道是哪个List,就用ArrayList。
 
    如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
 
    如果你知道用集合,就用ArrayList。
 
C:在集合中常见的数据结构(掌握)
    ArrayXxx:底层数据结构是数组,查询快,增删慢
    LinkedXxx:底层数据结构是链表,查询慢,增删快
    HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
    TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值