JAVA基础整合——集合框架2

Map接口

Map接口是一个双列集合(键值对集合),其中这个key要求必须保证唯一性,value可以重复。因为后面需要根据key来从容器中快速获取对应的那个元素。

  • Map接口下面的实现类由HashMap、TreeMap

1.HashMap

1>下面是HashMap具体使用过程中会用到的方法:

  1. put(key,value) 向Map集合中添加新元素
  2. size() 返回当前集合中的元素个数
  3. get(key)根据某个key获取对应的value
  4. containsKey(key) 判断某个key是否存在
  5. isEmpty()判断集合是否为空
  6. remove(key)根据key删除对应的元素
  7. clear()清空Map集合

2>Map中集合的遍历(2种):
Map集合不能使用普通for循环,因为无下标。

  1. 调用HashMap集合的keySet()方法获取键集合,返回Set集合,如:
Set<String> set = map.keySet();//拿到这个键集合就可以对HashMap中的元素遍历了
  1. 调用Map接口的entrySet()方法返回值一个Set,但是里面存储的是Map.Entry类型的对象,该类型是一个Map中的内部类,该类型的对象上提供两个方法getKey和getValue,如:
Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
    System.out.println(entry.getKey() + "--->" + entry.getValue());
}

3>HashMap的原理解析:

  1. jdk1.7中HashMap底层采用的是:数组+链表,在jdk1.8中底层采用的是:数组+链表+红黑树(二叉平衡树)
  2. putVal()过程如下:
    2.1 对添加元素的key调用hashCode()方法得到一个hash值
    2.2 根据哈希值结合一定的算法映射为一个数组下标
    2.3 如果这个下标对应的位置为null,直接存入该下标位置,如果这个下标对应的位置已经存在元素了(散列冲突),将添加的元素的key依次和当前位置的每个元素的key进行相等比较(equals),如果发现有相等,就用新的value覆盖旧的value,如果没有发现key相等的元素,则添加到链表末尾
    2.4 当某个桶位链表上的元素过多(8个)则将这个链表上的元素转成红黑树,目的提高查找效率。

3.HashMap底层代码重点属性分析:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//16  默认的初始化容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认的加载因子,决定扩容的边界  length*0.75
static final int TREEIFY_THRESHOLD = 8;//树化的阈值
static final int UNTREEIFY_THRESHOLD = 6;//树的降级阈值
static final int MIN_TREEIFY_CAPACITY = 64;//设置做小的树化容量  和 TREEIFY_THRESHOLD一起来决定是否转成红黑树
static class Node<K,V> implements Map.Entry<K,V> //HashMap集合中每个元素对应的节点类型
   final int hash;//当前元素的hash值
   final K key;//key
   V value;//value
   Node<K,V> next;//下一个节点的地址
transient Node<K,V>[] table;//Map集合中用来保存数据的数组,刚创建完map集合table默认为null
transient int size;//跟踪当前集合元素的个数
int threshold=length*DEFAULT_LOAD_FACTOR;//阈值,和扩容相关的

调用key的hashCode之后会对这个哈希码进行一个扰动处理static final int hash(Object key)这个方法是扰动处理方法,算法为:hash&(length-1),前提是length(数组长度)属性要为2的倍数。后面我自己对HashMap讲解会有一段话,里面有介绍为什么要是2的次方数。这个扰动处理是为了让散列变得更加的均匀。

  • 第一次putValue的时候创建一个长度为16的数组,扩容的边界阈值为12
  • HashMap里面的数组长度必须是2的次方数,为了保证散列的均匀。
  • hash&(length-1):散列均匀的算法
  • 数组扩容的策略是2倍关系
  • 加载因子0.75

下面这个内容是我自己对HashMap的理解,内容较多,请耐心推看:

  • 答:针对HashMap集合我个人是这么认为的:首先HashMap底层使用数组+链表+红黑树完成的;红黑树是什么树?它是一颗二叉平衡树。我们知道当我们去使用HashMap时,我们要先new空间,去创建对象然后使用,但是当我们去new空间时,会出现两个HashMap的构造函数一个是有参一个是无参的,有参情况下是当我们知道我们需要多大空间时直接定义空间大小,这里系统会把空间直接分配给我们。但是我们一般都是写无参构造,用无参构造时,系统会采去延迟策略给我们分配空间,这样的好处是合理的分配内存资源。这时系统会根据我们输入的元素个数,以合理的策略扩容。所以我们一般都会写无参构造来new空间。当我们把对象创建后,往这个对象put元素时会调用key的hashCode获得一个哈希码,获取之后会对这个哈希码进行扰动处理,怎么样的扰动处理呢?算法为:hash & (length-1),hash为hash值,为什么要与数组长度减1做与运算呢,这里其实有一个前提,这个数组长度必须是2的倍数,我们都知道2的次方数减1之后出现的一定是低位全1高位全0的二进制数,例如:0000000000000001111,拿数组长度16来说,去hash值按位与之后得到的数还是一个0-15的数。这时我们可以根据这个数进行占位,达到一个散列均匀的情况。new空间会出现如下的过程(无参构造):当我们第一次添加元素时,系统会在内存中给我们一块长度大小为16的数组,还会给我们定义一个加载因子,这个加载因子是决定了我们什么时候扩容,扩容时机是当我们数组有(当前数组长度*加载因子)个空间被占之后会调用指定方法为我们扩容,扩容后的大小是原数组的两倍,注意扩容后之后需要给前面已经占好空间的元素,进行重新的占位因为我们想要达到一中散列均匀的情况,让我们的元素均匀的分布在这个数组中。当我们一直往数组中添加元素肯定会造成一种散列冲突的情况,我们会根据比较元素的内容判断重复问题,如果这个数组中有一个位置的元素超过了8个且总共的容量达到64后,我们会对这个位置的元素进行树化,将它生成一颗红黑树(之前说红黑树我不知道这颗树是什么样树,但是说二叉平衡树我们就明白了它的作用了),为什么要要去生成这颗树了,因为当这个位置的元素变多之后如果我们想要去查找里面其中一个数据,查找起来的时间复杂度位O(n),这种效率是很低的,但是我们树化之后变成一颗二叉平衡树查找起来的效率就很高,每一次都是二分查找,所以时间复杂度为O(lon2n),所以我们在这里就有树化的必要了。这就是我对HashMap的理解!

2.LinkedHashMap

  • 是HashMap的一个子类,用来在HashMap的基础上保留了元素添加顺序。

3.TreeMap

用来按照大小顺序对元素进行排列,如下:

//创建一个HashMap
Map<String, String> map=new TreeMap<>();

//往Map集合中添加元素		

map.put("1004","zhaoliu");
map.put("1002","lisi");

map.put("1003","wangwu");

map.put("1001","zhangsan");

Set<String> set = map.keySet();
for (String string : set) {
    System.out.println(string);
}

面试题:Hashtable与HashMap的区别:
答:Hashtable是一个线程安全版的HashMap,HashMap是线程不安全的,Hashtable是线程安全类,HashMap元素key可以为空,Hashtable不允许。

3.Properties

它是Hashtable的一个子类,和Hashtable不同,Properties主要用来加载应用程序的配置信息,加载过程在整个程序运行期间只会发生一次。

  • 属性资源文件一般以.properties作为后缀,本质上就是一个文本文件,内容格式必须是key=value,如下:
//1.创建一个Properties集合对象
Properties prop=new Properties();
//2.需要有一个读取外部磁盘文件的输入流
InputStream in = TestProperties.class.getResourceAsStream("/info.properties");
System.out.println(in);
//3.调用properties集合对象的load方法
prop.load(in);
//4.调用prop集合对象的getProperty(String propertyName)
String username=prop.getProperty("username");
String password=prop.getProperty("password");

System.out.println("username:" +  username);
System.out.println("password:" +  password);

集合这个内容基本上总结到这里,或许可能还会有添加修改的地方。那最后再做一次总结

  1. 双列集合相对于单列集合来说更耗费内存空间,但是根据某个关键字查询的效率比较高;
  2. key的选择:
    2.1 key具有唯一性
    2.2 信息更简单,占的空间更少
    2.3 选择经常作为查询条件的字段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值