Map集合
Map接口的特点
- Map中key value可以是任意数据l类型,会封装到HashMap$Node中(接口的多态-是一种数据类型,HashSet的内部类)
- Map中不允许Key重复,value可以重复
- key可以为null,但只能有一个,value可以为null,并不影响
- key、value 是一对一的关系,通过key总能找到对应的value
- Map存放数据的key-value示意图,一对k-v是放在HashMap$Node中,因为Node实现了Entry接口
- 线程不安全
Map的底层:
table表(数组+双向链表+红黑树的结构)来组织HashMap$Node,把Node封装成Entry,存放到EntrySet集合中,除此之外可提供Keyset,可以单独把key存在Set集合中,value也是一样的提供一个values存储在Collection集合中
Map集合的常用方法
- put 添加
- remove 根据键删除值
- get 根据键获取值
- size 获取元素个数
- isEmpty 判断个数是否为0
- clear 清除
- comtainsKey 查找键是否存在
Map的六大遍历方式
// 方式一 通过Map.keySet使用iterator遍历
Iterator<String > iterator1 = map.keySet().iterator();
Iterator<String > iterator2 = map.values().iterator();
while (iterator1.hasNext()) {
String next = iterator1.next();
Object o = map.get(next); // 获取值
System.out.println(o);
// System.out.println(next);
}
// 方式二 通过Map.entrySet使用iterator遍历
Iterator<Map.Entry<String ,String >> entry = map.entrySet().iterator();
while (entry.hasNext()) {
Object next = entry.next();
System.out.println(next);
}
// 方式三 通过Map.keySet遍历
for (String key : map.keySet()
) {
System.out.println("key:" + key +" value:"+ map.get(key));
}
// 方式四 通过For-Each迭代entries,使用Map.entrySet遍历
for (Map.Entry<String , String > entry1: map.entrySet()
) {
System.out.println("key:" + entry1.getKey() +" value:"+ map.get(entry1.getKey()));
}
// 方式五 使用lambda表达式forEach遍历
map.forEach((k,v)-> System.out.println("key = "+k+",value="+v));
HashMap底层原理
结论
扩容机制:与HashSet完全一直
-
HashMap底层维护了Node类型的数组table,默认为null
-
当创建对象时,将加载因子初始化为0.75
-
当添加key-value时,通过key的hash值得到table的索引,然后判断索引是否有该元素,如果没有就添加,如果有就通过equals判断key是否相等,如果相等,就替换val值。如果不想等需要判断是树结构还是链表结构,做出相应的处理,如果添加时发现容量不够,就会进行扩容。
-
第一次添加,需要扩容table容量为16,临界值为12
-
以后扩容则需要扩容table值的两倍32,临界值为原来的2倍,24,一次类推
-
在java8之后,如果一条链表的元素超过8 ,并且table的大小超过64就会树化(红黑树)
// 树化的条件 // 扩容条件 if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); // 树化代码 else if ((e = tab[index = (n - 1) & hash]) != null) { TreeNode<K,V> hd = null, tl = null; do { TreeNode<K,V> p = replacementTreeNode(e, null); if (tl == null) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); if ((tab[index] = hd) != null) hd.treeify(tab); }
HashTable
1. 结论
- HashTable的键和值都不能为null
- HashTable是线程安全,HashMap是线程不安全
- 使用方法和Hashmap使用基本一致
- 初始大小为11,扩容2n+1,临界值也是容量*0.75
properties
- 继承HashTable并实现了Map接口,也是键值对形式来存储数据。
- 特点与HashTable类似
- properties还可以用于从xxx.properties文件中,加载数据到properties类对象,并进行读取和修改
- xxx.properties文件通常用于配置文件,与io流有关
工作中集合的选择
集合的使用
- 先判断存储的类型(一组对象或一组键值对)
2.一组对象:Collection接口 允许重复: List
3.增删多: LinkedList (OBject [] elementData) [底层维护了一个双向链表]
4.改查多: ArrayList [底层维护Object类型的可变数组] 初始值:10 初始值:10 扩容大小
1.5
ArrayList 调用的是Arrays.CopyOf方法,当插入数据,导致size + 1 >
elementData.length,也就是目前数组长度不够时,需要进行扩容。
Vector:在多线程中使用,扩容机制为初始容量10,扩容大小为2n
不允许重复: Set;
无序: HashSet [底层是HashMap,维护了一个哈希表即(数组+链表+红黑树)]
排序: TreeSet
插入和取出顺序一致: LinkedHashSet,维护数组+双向链表
3. 一组键值对:Map
键无序: HashMap[底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树]
1. HashMap扩容 第一次扩容为16,加载因子为0.75,扩容发生为加载因子*容量,达到这个值就进行
扩容,当一条链表的数量达到8也会进行数组扩容,当数组扩容到64,就会进行红黑树化,之后数组不再
进行扩容。
键排序: TreeMap
键插入和取出顺序一致: LinkedHashMap
读取文件:Properties
### TreeSet
* 最大 特点:可以排序
* 底层是TreeMap
```java
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
自定义TreeMap排序
package JavaTest;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
// 匿名内部类重写compareTo排序规则
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// return ((String) o1 ).compareTo((String) o2);
// 按照长度大小进行排序,规则长度相等就是相同元素
return ((String) o1 ).length() - ((String) o2).length();
}
});
treeSet.add("aaaa");
treeSet.add("lzq");
treeSet.add("zs");
treeSet.add("ls");
System.out.println(treeSet);
}
}
底层源码的实现机制
- 核心代码
1. 构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 在 调用 treeSet.add("tom"), 在底层会执行到
if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
do {
parent = t;
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果相等,即返回 0,这个 Key 就没有加入
return t.setValue(value);
} while (t != null);
}
TreeMap
- 核心代码
1. 构造器. 把传入的实现了 Comparator 接口的匿名内部类(对象),传给给 TreeMap 的 comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 调用 put 方法
2.1 第一次添加, 把 k-v 封装到 Entry 对象,放入 root
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
2.2 以后添加
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do { //遍历所有的 key , 给当前 key 找到适当位置
parent = t;
cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的 compare
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不
return t.setValue(value);
} while (t != null);
}
*/
Collections工具类
介绍
- Collections是一个操作Set、List、Map等集合的工具类
- 提供了一系列的静态方法,对集合元素进行排序,查询、修改等操作
常用方法
- reverse(list) 反转集合中元素的顺序
- shuffle(list) 对list集合元素进行随机排序
- sort(list)根据元素的自然顺序对指定list集合元素进行升序
- swap(list , int i, int j ) 根据制度list集合中的i出元素 , 和j出元素进行交换顺序
- max(list) 根据元素的自然顺序,返回给定集合中的最大元素
- max(list,new Comparator) 根据Comparator指定的顺序,返回给定集合中的最大元素
- min(list)
- ming(list,Comparator)
- copy(list,dest,list src) 将src中的内容赋值到desc中,dest中的空间不能小于src集合的空间,否则会报数组长度越界,IndexoutOfBoundsException
- replaceAll(list,oldVal,newVal);