java HashMap、ArrayList

数据结构

java只提供了两种基础数据结构:

  1. 单个变量
  2. 数组

之后的各种的数据结构,都是对这两种基础数据结构类型的管理;

ArrayList

本质是一组连续的数组;

new 的时候会先开辟一组空间,先占用着,不够用了再扩容;

扩容时:

  1. 先去找连续的空闲的合适大小的一组内存空间
  2. 老的数据先用深拷贝至新的长数组中
  3. 删除老的数组

缺点:在中间指定位置插入数据时,后面所有元素都得往后移动,效率低;

优点:查询快,直接用下标就可以找到元素;

LinkedList

本质是单个变量;不用连续空间;

用的是双向链表结构:多个元素时,会在当前元素持有下一个元素的地址,并且当前元素还会持有上一个元素的地址;

缺点:查询慢;查询方式:a=总长/2; 与要查的下标index进行比较,a大于index,则从头根据元素中的地址开始一个一个查,小于,则从尾根据元素中的地址开始一个一个查;

优点:在中间指定位置插入数据时,效率高;

性能提升

  1. 用时间换空间
  2. 用空间换时间

经典面试题:

在不用第三个变量的情况下,将两个变量值进行交换;

//使用第三方变量temp:使用额外的空间来减少计算时间
int a;
int b;
int temp;

temp=a;
a=b;
b=temp;

//不使用第三方变量:增加计算时间来减少使用空间
int a;
int b;

a=a+b;
b=a-b;//a+b-b=a
a=a-b;//a+b-a=b

Hash函数

对象的hashcode的高16位和低16位进行“异或”运算(不同则为1,相同则为0);

int h = obj.hashCode();

int hash = h ^ (h >>> 16);


HashMap

数组+单向链表;将数据分为N个链表;链表的头放在数组中;

  1. key-value储存;
  2. key唯一;
  3. 初始长度16;
  4. 判断是否扩容的阈值为:当前长度*0.75f;(0.75f为国际上测试的最适合的值)
  5. 用key计算的同一个index放在同一个链表中;链表的第一个放在数组中;

参考:为什么HashMap的加载因子是0.75? - 沐雨橙风~~ - 博客园

计算下标index:

  1. key用hash函数计算得到hash值;
  2. 得到的hash值与(数组长度-1)进行“与&”运算(都为1则为1,其他为0)

长度-1的二进制都为11111时,才能得到唯一的一个下标;

所以HashMap的长度都是2的幂次方; (2的幂次方-1)得到的数据都是11111之类的;

若不是“1111”之类的,会造成不同的key会落到同一个下标,造成hash冲突;

确定容量:

  1. 输入一个长度数字
  2. 取数字最高位的第一个1;
  3. 将1之后低位的全变为1;
  4. 将得到的这种“111111”加1;
  5. 得到的数“1000000”就是2的幂次方

为什么是2倍扩容?

为了解决hash冲突问题,但是没有完全解决;

JDK1.7的HashMap死环问题:

  1. 扩容时,头插法进行插入数据时,完成数据替换后,会导致引用顺序颠倒
  2. 在多线程切换时,可能会发生相互引用,发生死环

JDK1.8解决办法:

  1. 采用尾插法
  2. 加入高低位分开插入
  3. 结束后,断掉尾部引用
  4. 链表长度大于8时,转换为红黑树

红黑树:

【老实李】JDK1.8中HashMap的红黑树 - 简书

 HashMap简述及红黑树 - sys_user_findnull - 博客园

HashSet

只储存对象,对象不能重复,使用对象计算hash值;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值