集合内容详解

数组

  1. 长度开始时必须指定,而且一旦指定,不能更改
  2. 保存的必须为同一类型的元素
  3. 使用数组进行增加/删除元素的示意代码-比较麻烦

二十四、集合

  1. 可以动态保存任意多个对象
  2. 提供了一系列方便的操作对象的方法
  3. 使用集合添加删除新元素的示意代码-简洁明了

集合主要是两组:单列集合,双列集合

Collection接口有两个重要子接口list set,他们的实现子类都是单列集合

Map接口的实现子类时双列集合,存放的k-v

Collection实现接口的特点:

  1. collection实现子类可以存放多个元素,每个元素可以是Object
  2. 有些Collection的实现类,可以存放重复的元素,有些不可以
  3. 有些Collection的是西安类,有些事有序的(List),有些不是有序的(set)
  4. Collection接口没有直接的实现子类,时通过它的子接口set和list来实现的

ArrayList的本质就是一个数组

ArrayList arrayList = new ArrayList();
arrayList.add("jack");
arrayList.add(10);//list.add(new Integer(10))本质上就是自动装箱
arrayList.add(true);
System.out.println(arrayList);

Colleact接口遍历元素方式1-使用Iterator迭代器

  1. Iterator对象称之为迭代器,主要用以遍历collection集合的元素
  2. 所有是西安类collection接口的集合类都有一个iterator方法,用以返回一个实现iterator接口的对象,即可以返回一个迭代器
  3. Iterator的结构
  4. Iterator仅仅用于遍历集合,它本身并不存放对象

hasNext()判断是否还有下一个元素

Next()作用1.下移 2.将下移以后集合位置上的元素返回

在调用用next方法前要调用hasnext,否则要是下边没东西会出exception

快捷键itit

如果希望再次遍历,需要重置我们的迭代器iterator = arrayList.iterator();

第二种遍历对象的方式-for循环增强

底层还是迭代器

List接口基本介绍:

List接口是collection接口的子接口

  1. List集合类中元素有序(即添加顺序和去除顺序一致)、且可重复
  2. List集合中每个元素

List 中set就是替换   sublist取自集合是前闭后开

ArrayList注意事项:

  1. ArrayList可以加入null,并且多个
  2. ArrayList是由数组实现数据存储的
  3. ArrayList基本等同Vector,除了ArrayList是线程不安全(执行效率高)看源码,在多线程情况下,不建议使用ArrayList·

ArrayList底层结构和源码分析

  1. ArratList维护了一个Object类型的数组elementData   

transient Object[] elementDate//transient表示瞬间短暂的,是一个修饰符,表示这个被修饰的属性不被序列化

  1. 创建ArrayList对象时,如果使用无参构造器,第一次添加,则扩容elementdata为10,如果在其扩容,则扩容elementDate为1.5倍
  2. 如果使用的时指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,这届扩容elementData为1.5倍

源码中modcount是集合被修改的次数,目的是防止有多个线程取同时修改它

如果elementDate的大小不够就调用grow去扩容

第一次扩容是10后面是1.5倍》》是位运算,就是变成原来的0.5倍,扩容使用的是Arrays.copyOf(保证原先数据还在)

  1. 如果有参数构造器程序员指定elementDate大小,地磁扩容就按照elementDate的1.5倍扩容,其他的和3一样

Vector底层剖析:

  1. Veactor底层也是一个对象数组 protected Object[] elementDate
  2. Vector是线程同步的,即线程安全,有synchronized关键字
  3. 底层一夜是可变数组,安全但是效率不高,如果是无参,默认是10,满后2倍扩容,如果指定大小,则每次直接按2倍扩
  4. capacityIncrement是一个参数,里面是自定义增量

LinkedList:

  1. 底层是实现了一个双向链表和双端队列特点
  2. 可以添加任何元素,包括null
  3. 线程不安全,没有实现同步

底层机制:

  1. 底层维护了一个双向链表
  2. 两个属性first和last分别指向首节点和尾节点,里面又维护了prev、next、item三个属性
  3. Linklist的添加和删除,不是通过数组完成的,相对来说效率较高

LinkedList底层结构

Node构造器Node(Node prev , E element , Node next)

Remove自动返回被删除的对象

Set同理,在替换之后会返回原本的value(这两个都可以用Object接收)

因为LinkList是实现list接口,所以可以使用其遍历方式

ArrayList底层结构是可变数组,增删效率较低因为要数组扩容,改查效率高

LinkedList双向链表, 增删效率较高,改查效率较低

在实际开发中大部分都是查询,因此大部分选择ArrayList

Set接口基本介绍

  1. 无序(添加和取出数据不一样,没有索引,但它是固定的不会一会一变)
  2. 不允许重复元素,所以最多包含一个null

和list接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection的子接口一样

所以可以用1.迭代器 2.增强for 3.不能使用索引的方式来获取

Set的底层是一个数组+链表

hashSet的全面介绍:

  1. HashSet底层其实是一个hashmap(hashmap的底层是数组+链表+红黑树这个存储效率很高)
  2. 同理不同有重复元素,只能有一个null
  3. 添加一个元素时,先得到hash值-会转换成-索引值
  4. 找到存储表table,看这个索引位置是否已经存放有元素
  5. 如果没有,直接放入
  6. 如果有,调用equals比较(比的是谁,程序员说的算),如果相同,就放弃添加,如果不相同,就加到最后
  7. 在jdk8中,如果一条链表的元素个数到达了默认值8,并且table的大小》=64就会进行树化(红黑树)

Hash()能够得到一个key对应的hash值h = key。HashCode^(h>>16)为了防止让这个数不容易出现重复//^这个是异或

Resize()扩容

PRESENT是占位object,后面的value也是它

Node<K,V>[] tab; Node<K,V> p; int n, i;//定义了辅助变量

if ((tab = table) == null || (n = tab.length) == 0)//table就是HashMap得一个数组,类型是Node[],一开始table肯定是空的

DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16  1向右左移4位就是1×2×2×2×2=16

newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//thr就是临界值=默认临界因子(0.75)×默认初始容量(16)同理,下一次数组到达临界值12,就会扩容到16*2=32.新的临界值就是32*0.75=24

threshold = newThr;//threshold域,界

Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//通过上面计算的新容量,建立新数组

第一个if的作用就是当前的table如果是null,或者,其大小为0,就进行第一次扩容,到16个空间

if ((p = tab[i = (n - 1) & hash]) == null)//&位运算符

根据key得到的hash去计算该key应该存放到table表的哪个所引位置,并把这个位置的对象,赋给临时变量p,

判断p是否为null,

tab[i] = newNode(hash, key, value, null);

如果为null,表示还没有存放元素,就创建一个Node(value=PRESENT,next = null)

返回空表示成功,返回旧值表示添加失败

if ((p = tab[i = (n - 1) & hash]) == null   不为null时,有三种情况

科普(key一样的,这个hash一定一样,但是不一样也有可能一样)

第一种:当前索引位置对应的链表的第一个元素和准备添加的key的hash的哈希值一样,并且满足第一个元素(p)与添加的key是同一个对象或者key不等于空且key的内容与p.key的内容相同(取决你重写的equals)

第二种:判断p是不是一颗红黑树,是的话,就按照红黑树的方式进行比较,调用putTreeVal(this,tab,hash,key,value)

第三种:上面两种都不满足的话就是第三种情况,一次和该链表的每个元素比较后,都不相同,则加入到该链表的最后(在把元素添加到链表后,立即判断该链表是否达到8【0-7】个节点,达到了就调用treeifyBin(tab, hash);

对当前这个链表进行树化转成红黑树,在转换成红黑树时,如果表的length达到64,才能真正树化,如果没达到,就对table表进行扩容);如果在比较过程中有相同的情况,直接break;

树化的条件是:既要链表长度达到8,也要table数组长度达到64

TREEIFY_THRESHOLD(链表的树化默认临界值是8)

MIN_TREEIFY_CAPACITY(table树化的默认长度)

当我们向hashset种增加一个元素,加入table表(在链表后面也算)就算是增加了一个

面试题:

这句话是hashcode里说的

  • 如果两个对象根据equals(Object)方法相等,则对两个对象中的每个对象调用hashCode方法必须生成相同的整数结果。

所以            String hhh = new String("hhh");
String hhh1 = new String("hhh");

System.out.println(hsp.equals(hsp1));//true 这是因为String的equals是比内容的,因此他俩的hashcode也是相同的,因此在哈希表里不能重复出现

但是这俩地址不一样,System.out.println(hhh==hhh1);//false

hashset仍然只有一个hhh得到了解释

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值