Java之Collection:List派系和Set派系

学习这一节,请先学习:https://www.jianshu.com/p/1dbf1be8006b

将List理解为链条、列表:按顺序

将Set理解为集、容器:没有顺序

1. List派系

List接口派系,继承Collection接口,下面有很多实现类

特点:有序,索引,可重复,取出方式有索引、 Iterator和增强for

常见实现类:ArrayList、LinkedList、Vector


1.1 List的特有抽象方法

一般来讲,在List的api手册中,方法中带有index的方法就是List特有的抽象方法,例如add方法:

boolean add(E e)

void add(int index, E element)


1.1.1 void add(int index, E element)

带index的add是List的特有方法,按序插入集合

另外,带有索引的操作都要防止越界操作,index的范围是0~集合的size


1.1.2 E remove(int index)

删除集合中位置为index的元素,返回删除的元素值


1.1.3 E set(int index, E element)

修改集合中的元素,返回修改前的元素,要防止越界操作


1.1.4 List的遍历

方式一:使用for循环 带索引的遍历

方式二:增强型for循环,foreach

方式三:集合通用的Iterator


1.2 迭代器的并发修改异常

复习:基本类型的等值比较可以用 ==, 引用类型使用equals()方法

可见,在集合的迭代中,如果向集合添加元素,会报出集合并发的修改异常

因此在迭代集合过程中,不允许修改集合长度



1.3 List集合的存储结构

List接口下有多个集合,他们的存储元素所采用的结构方式是不同的,这样就导致了它们有各自的特点,下面依次介绍:

1.3.1 堆栈stack

特点:

    FILO

    栈的出入口都是栈的顶端位置

    压栈存元素:把元素存到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置

    弹栈取元素:把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置


1.3.2 队列queue

特点:

    FIFO

    队列的入口、出口各占一侧。


1.3.3 Java的数组array

特点:

    查找速度快

    增删元素慢(只针对java,因为java的数组是固定长度的)

        指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引位置

指定索引位置删除元素:   需要创建一个新数组,把原数组元素根据索引,复制到新数组对应位置原数组中指定删除元素不复制到新数组中


1.3.4 链表link list

特点:

    多个节点间通过地址进行连接

    查找元素慢:查询时,需要通过连接的节点依次向后查找

    增删元素快


1.3.5 总结

到底采用哪一种方式要根据实际需求:

    查询得多:选择数组

    修改的多:选择链表

    传输的多:队列

    要求效率:选择堆栈


1.4 ArrayList集合的特点

需要频繁查询的操作,用ArrayList


ArrayList看名字就知道数据结构是数组类的

但它数组长度可变,多线程不安全,查询速度快

看源码可见:

    可以存储任何Object

    定义时可以指定长度也可不指定,一般不指定长度初始容量10

    添加元素时,如果超过当前容量(10)就要做扩容:Arrays.copyOf()


1.5 LinkedList

1.5.1 集合的特点

需要频繁增删的操作,用LinkedList


LinkedList看名字就知道数据结构是链表类的

元素长度可变,多线程不安全,查询速度慢,增删快

提供了大量的首尾操作方法


1.5.2 特有方法

addFirst, addLast  
getFirst, getLast
removeFirst, removeLast
pop, push


1.6 Vector集合(了解一下最早的JDK1.0版集合)

Vector也是List派系的数组结构方式,且是JDK中最早提供的集合,是一个同步的线程安全集合,Vector已经淡出JDK了,但是List由Vector改进而来,ArrayList跟它也相似

ArrayList完全可以替代Vector


2. Set派系

Set接口派系,继承Collection接口,下面有很多实现类

特点:无序,无索引,不可重复,取出方式只有Iterator和增强for

常见实现类:HashSet、LinkedHashSet

Set集合的方法和Collection接口的方法一模一样

由于之前详细介绍了List派系,Set派系很多方法使用上和List派系相似,这里就主要介绍新的概念


2.1 存储和迭代

存储是不重复的,且取出时发现是无序的,没有索引的概念


2.2 底层数据结构 哈希表(hashTable)

哈希表又叫链表数组结合体,java中默认数组长度应该是16,加载因子是0.75

特性:

存储、取出速度快

不同步:线程不安全

HashSet在构造器中,默认是长度为16的初始数组(List则为10)

加载因子是0.75:意思是如果数组占用长度达到16*0.75,也就是12的时候,数组就要进行扩容,由16扩容到32(rehash)


2.3 对象的hash值: public int hashCode()

对象的哈希值就是一个普通的十进制整数,源于父类Object

2.3.1 hashSet中没有重写hashCode

Object的hashCode返回一个系统指定的hash值


2.3.2public int hashCode()方法可重写

先看一段String类的hashCode测试代码:

String的hashCode

可见String的hashCode被重写过,见源码:

根据源码可以手动算出"abc"的hashCode就是96354


2.3.3 String类的hash存储原理

存储步骤:

1. 调用对象的哈希值 new String("abc").hashCode ----> 96354

2. 集合在容器内找有没有和95354一样的hash值

3. 如果没有相同hash值就往hash容器中存储

4. 如果有相同hash值,集合会让后来的对象new String("abc").equals(已经有的对象),如果再次相同才判定新来的对象是重复元素(因为又可能hash值相同,存储的内容不同)


结论:

如果是String类,由上面步骤可见,如果hash值不同,则存到hashSet的数组中(数组默认长度16,加载因子0.75)

如果hash值相同,equals不同,则存到拥有相同hashCode的数组中,在最后新增链表

如果hash值和equals都相同,则被判定为相同元素,不存储


2.3.4 自定义类的hash存储(了解原理)

自定义的类,hashCode是不同的

2.3.3 讲了一大堆原理,就是为了在这里重写方法

如果希望姓名年龄相同的对象在add到HashSet是同一个对象的话,需要重写hashCode和equals方法

重写hashCode:

由于String重写hashCode的源码比较麻烦,这里我们简化一下:

public int hashCode() {

    return name.hashCode() + age;

}

但是这样会有一个问题,如果不同name和age计算出来的hash值相同就不妥了,所以重写equals方法来双重验证。


重写equals方法:

public boolean equals(Object obj) {

    if(this == obj)

        return true;

    if(obj == null)

        return false;

    if(obj instanceof Person) {

        Person p = (Person)obj;

        return name.equals(p.name) && age == p.age;

    }

    return false;

}

完整代码:

Person类:

package com.gamebear.s24;

public class Person {

    private String name;

    private int age;

    public Person() {}

    public Person(String name, int age) {

        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;

    }

    public String toString() {

        return "Person [name=" + name + ", age=" + age + ", hash=" +         this.hashCode() + "]";

    }

    public int hashCode() {

        return name.hashCode() + age;

    }

    public boolean equals(Object obj) {

        if(this == obj)

            return true;

        if(obj == null)

            return false;

        if(obj instanceof Person) {

            Person p = (Person)obj;

            return name.equals(p.name) && age == p.age;

        }

        return false;

    }

}

主程序:

程序可见:

    相同的名字和年龄,没有被重复写入

    且名字和年龄不同,hash值相同的情况,都被写入了


2.3.5 提高重写hashCode、equals的效率(了解原理)

在2.3.4中,由于hash值相同的概率相对还是比较高,这样equals运算相对比较频繁。

方法:尽量让hash值相同的概率降低,让age乘以一个非0,1的正整数,例:

public int hashCode() {

    return name.hashCode() + age*55;

}


2.3.6 推荐使用eclipse的插件重写hashCode和equals方法(编程使用方法)

鼠标右键
eclipse自动帮你重写hashCode和equals方法


2.4 LinkedHashSet

LinkedHashSet继承自HashSet,实现Set接口

特性:

    具有顺序:存储和取出的顺序是相同的

    不同步:线程不安全


2.5 判断集合元素唯一的原理

由于八个基本数据类型都重写了hashCode和equals方法,这样就保证了将数据添加到Set集合中时帮你过滤了重复元素


由于contains源码中,最终是使用equals去判断元素是否重复,

因此,自定义类的情况下,需要注意每次创建类时,使用2.3.6的方式重写hashCode和equals方法

2.5.1 ArrayList的contains方法

boolean contains(Object o)


2.5.2 HashSet的add/contains方法

boolean add(E e)

boolean contains(Object o)

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

推荐阅读更多精彩内容

  • Java集合类可用于存储数量不等的对象,并可以实现常用的数据结构如栈,队列等,Java集合还可以用于保存具有映射关...
    小徐andorid阅读 1,936评论 0 13
  • 很少有几个这样的作家,经常把自己的尸体掩埋起来,接着开始另一个新阶段。而每一次都是出于真正的困境。 “危机作家” ...
    Lynn_X阅读 365评论 0 0
  • 你是在期待落霞为你送上红色的祝愿还是在等太阳升起再次为你护航2017.12.22 Ask ship You're ...
    失业猎手阅读 201评论 0 3
  • 不知道为何想到这个题目,大晚上突然睡不着是真的。我怎么了,是不是被好多个漩涡包围着所以才会失眠了。细细想来...
    suki小希阅读 421评论 0 0