从Android到Java(二)

Java基础知识填坑继续。

Java 集合与泛型

数组 VS ArrayList

数组大概是我们学习任何语言时接触到的第一个集合

        String[] strs = new String[10];
        strs[0] = "a";
        strs[1] = "b";

        List<String> lists = new ArrayList<>();

数组也是对象;相较于普通的数组,ArrayList在创建时不必指定大小,会在进行增删操作时动态的调整自己的大小。

数组与List相互转换
        //数组转换为List
        lists = Arrays.asList(strs);
        //List 转 数组
        strs =  lists.toArray(strs);

将集合中的对象进行排序

使用Collections.sort()方法对集合中的对象进行排序的两种方式。

  • 该对象实现了Comparable接口,明确指定了排序方式。
public class Student implements Comparable<Student>{
    private String name;
    private int id;

    public Student(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public int compareTo(Student student) {
        //按照name(字符串值)从小到大排序
        return name.compareTo(student.name);
    }
}

这样,由Student对象构成的集合就可以使用Collections.sort()方法进行排序了。

        List<Student> students = new ArrayList<>();
        for(int i=0;i<10;i++) {
            Student student = new Student(i + "-name", i);
            students.add(student);
        }
        Collections.sort(students);
  • 实现Comparator接口,动态定义排序方式。
    private static class ComprareByName implements Comparator<Student> {
        @Override
        public int compare(Student student, Student t1) {
            //按name 从大到小进行排序
            return t1.getName().compareTo(student.getName());
        }
    }

这样就可以使用重载的sort方法进行排序

        Collections.sort(students,new ComprareByName());

Comparator的compare实现方式会覆盖集合元素中默认的实现,也就说虽然Student内部是从小到大排序,但会被这里ComprareByName的实现所覆盖

为了使集合有序,我们也可以使用TreeSet。

TreeSet 以有序的状态存储元素,并防止重复。

因此,为了正确的使用TreeSet 需要注意以下两点:

  • 同上面第一点,加入到TreeSet集合中对象(元素)必须实现了Comparable接口。
  • 使用重载,用Comparator参数的构造函数来创建TreeSet。
        TreeSet<Student> mStudents = new TreeSet<>(new ComprareByName());

集合分类

  • Collections
    • List 索引位置明确的集合
    • Set 不允许重复元素的集合
  • Map 使用成对key和value的集合

以上三种集合的类图如下:

Java 集合框架简图

从中可以看到我们常用的一些类,如ArrayList,HashMap 等。

如何检查对象的重复性?如何判定两个对象相等?

        Student a = new Student("a", 1);
        Student b = new Student("b", 2);
        Student c = a;
        Student d = new Student("b", 2);

        System.err.println("a.hashCode()="+a.hashCode());
        System.err.println("b.hashCode()="+b.hashCode());
        System.err.println("c.hashCode()="+c.hashCode());
        System.err.println("d.hashCode()="+d.hashCode());

输出

a.hashCode()=1118140819
b.hashCode()=1975012498
c.hashCode()=1118140819
d.hashCode()=1808253012


通过打印a,b,c,d 四个对象的hashcode 值,可以看到引用变量a和c 指向的是堆上的同一个对象,因此他们的hash值必然是相等的。引用对象b和d虽然创建的对象内容是一致的,但他们任然是分别指向两个不同的对象,因此hash值也是不同的。

下面我们用equals 方法比较一下这四个对象

        System.out.println("a.equals(b) " + a.equals(b));
        System.out.println("a.equals(c) " + a.equals(c));
        System.out.println("b.equals(d) " + b.equals(d));

输出

a.equals(b) false
a.equals(c) true
b.equals(d) false

结果很明显,因为Object的equals 默认执行的是对象引用是否相等的比较,因此b.equals(d)的结果为false。

    public boolean equals(Object obj) {
        return (this == obj);
    }

但是,从我们创建对象的代码可以得知,b和d 这两个对象内容是一样的;因此,这两个对象就应该是同一个,他们应该是相等的;因此,我们可以覆盖默认的hashCode()和equals()方法。

public class Student implements Comparable<Student>{
    private String name;
    private int id;


    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        Student mStudent= (Student) o;
        return getName().equals(mStudent.getName());
    }

    public String getName() {
        return name;
    }
}

现在再次测试,就可以看到b.equals(d)的结果为true了。

因此,我们可以得出以下结论:

  1. 如果两个对象相等,则hashcode值必须相等
  2. 两个对象的hashcode值相等,他们也不一定是相等的。(当然,这种几率应该很小)
  3. equals()默认执行的是== 比较,也就是说会去测试两个引用是否对堆上同一个对象引用;因此,对于我们自己创建的对象,应该同时覆盖equals()方法和hashCode()方法,规范检测对象一致性的标准。

泛型

使用泛型可以构建出类型更加安全的集合,让问题尽可能的在编译期就被发现,而不是等到了执行期才冒出来

public class ArrayList<E> extends AbstractList<E> implements List<E>

以常用的ArrayList为例,使用泛型后代表以后所有对ArrayList的操作,添加,删除或返回的对象类型都是E,不能是其他类型。

        List<Student> students = new ArrayList<>();
        for(int i=0;i<10;i++) {
            Student student = new Student(i + "-name", i);
            students.add(student);
        }

students 中添加的元素只能是Student类型的,不能是其他;同时从students结合中获取到的对象也一定是Student类型。

从泛型的角度看,extends和implements是等价的,都表示当前类是一个……。对ArrayList来说,他既是一个AbstractList,也是List。

当泛型遇到多态

现在有Student的子类:SeniorStudent和CollegeStudent。

    private static void printStudents(List<Student> students) {
        for (Student mStudent : students) {
            System.out.println(mStudent.getName());
        }
        //面对这样的情况,这个方法只能接受List<Student>类型的参数
        students.add(new SeniorStudent("hacker", 999));
    }

    public static void main(String[] args) {
        ArrayList<Student> students = new ArrayList<>();
        //因为泛型,List现在可以接受所有Student类型的对象
        students.add(new Student("mike",001));
        students.add(new CollegeStudent("lucy",002));
        students.add(new SeniorStudent("tom", 003));
        //
        printStudents(students);

        List<CollegeStudent> colleges = new ArrayList<>();
        colleges.add(new CollegeStudent("a", 100));
        colleges.add(new CollegeStudent("b", 101));
        colleges.add(new CollegeStudent("c", 102));
        //这样做是不行的
        printStudents(colleges);

    }

在上面的代码中,printStudents(List<Student> students),我们可以这样

printStudents(ArrayList<Student>)

但却不能这样

printStudents(List<CollegeStudent>)

在泛型方法中,参数中的集合可以是多态,但集合中的对象不能是多态。其中的道理我们通过printStudents 方法中最后一行语句很容易理解。因为你不能保证使用集合的方法,会对集合做怎样的操作。为了保证集合的安全性,这是很好的做法。

但是,这样不就丧失了多态的意义吗?如果不能用子类作为集合的类型,那难道要为每一个Student的子类型,单独写一个printStudents()方法吗? 其实不必,只要做如下改动即可:

    private static void printStudents(List<? extends Student> students) {
        for (Student mStudent : students) {
            System.out.println(mStudent.getName());
        }
        //当使用通配符声明后,将不能再向集合中添加元素,因此以下语句非法
        students.add(new SeniorStudent("hacker", 999));
    }

这种情况虽然从语法角度看似合理,但编译器会帮我们做限制,限制再次修改集合中的元素

这样printStudents(List<CollegeStudent>)就变得合法了。

当然,为了更容易理解,也可以这样声明:

    private static <T extends Student> void printStudents(List<T> students) {
        for (Student mStudent : students) {
            System.out.println(mStudent.getName());
        }
        //当使用通配符声明后,将不能再向集合中添加元素,因此以下语句非法
        students.add(new SeniorStudent("hacker", 999));
    }

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,555评论 18 399
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 1,149评论 0 16
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,057评论 0 62
  • 第十天 权限修饰符 public protected default private 同一类 true true ...
    炙冰阅读 523评论 0 1
  • 带宝宝到店里来,隔壁有个小丫头,和宝宝差不多大,年龄相仿,应该可以一起愉快地玩耍。 小丫头刚进店门,看见我闺女手里...
    李慧英_c3a3阅读 520评论 5 6