Map
Map接口
定义
键值对的集合|映射的集合
map中键值对无序的。键唯一,一个key只能对应一个value
键值对
K --> V
key : 无序的,去重的|唯一的 --> Set集合
value : 可重复,无序的 --> Collection集合
常用方法
1)V put(K key, V value) 将指定的值与此映射中的指定键相关联(可选操作)。==key相同value覆盖==
2)boolean containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。
3)V get(Object key) 返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。
4)static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2) 返回包含两个映射的不可修改的映射。
Map<String,Integer> map2 = Map.of("zhangsan",18,"lisi",19);
5) default V replace(K key, V value) 仅当指定键当前映射到某个值时,才替换该条目的条目。
6) V remove(Object key) 如果存在,则从该映射中移除键的映射(可选操作)。
System.out.println(map.remove(1002));
System.out.println(map);
7) nt size() 返回此映射中键 - 值映射的数量。
System.out.println(map.size());
遍历方式
1)Collections values() 获取map集合中所有键值对的value;
2)Set keySet() 获取map集合中所有键值对的key,可以根据key获取value;
Set<Map.Entry<K,V>> entrySet() map集合中的每一个键值对转为Map.Entry<K,V>类型,多个Map.Entry类型的数据放在一个Set集合中;
/*
Map 遍历方式 :
1.Collection<V> values() 返回此映射中包含的值的Collection视图。
2.Set<K> keySet() 返回此映射中包含的键的Set视图。
3.Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射的Set视图。
*/
public class Class002_Each {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1001,"张三");
map.put(1003,"王五");
map.put(1004,"老薛");
map.put(1002,"Lisa");
map.put(1005,"Lisa");
System.out.println("----------------values()----------------");
Collection<String> values = map.values();
for(String str:values){
System.out.println(str);
}
System.out.println("-------------keySet()-----------------");
Set<Integer> keys = map.keySet();
Iterator<Integer> it = keys.iterator();
while(it.hasNext()){
Integer key = it.next();
String value = map.get(key);
System.out.println(key+"--->"+value);
}
System.out.println("-------------entrySet()-----------------");
Set<Map.Entry<Integer,String>> entrys = map.entrySet();
for (Map.Entry<Integer,String> entry:entrys){
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
}
TreeMap
基础知识
存储键值对数据,key是唯一的,去重的,无序的(存放的顺序与内部真实存储的顺序不保证一致)
1).底层结构 : 红黑树。TreeSet底层就是由TreeMap维护的
2).特点 : 根据key做自动升序排序(排序的规则由key的比较器决定)
3).去重排序 : 根据键值对的key实现,根据key进行比较实现去重,实现排序,与 value无关
4).应用场景 : 存储键值对数据的前提下,数据根据指定的规则,根据key实现自动升序排序,适合使用TreeMap
5).构造方法
TreeMap() 使用其键的自然顺序构造一个新的TreeMap
TreeMap(Comparator<? super K> comparator) 构造一个新的空TreeMap,根据给定的比较器排序。
6).新增方法 : 新增了一些与比较大小相关的方法
1). ceilingKey(K key) 返回大于或等于给定键的 null键,如果没有这样的键,则 null 。
2).K floorKey(K key) 返回小于或等于给定键的最大键,如果没有这样的键,则 null 。
3).K higherKey(K key) 返回严格大于给定键的最小键,如果没有这样的键,则返回 null 。
4).K lowerKey(K key) 返回严格小于给定键的最大键,如果没有这样键,则返回 null 。
5).K firstKey() 返回此映射中当前的第一个(最低)键。
6).K lastKey() 返回此映射中当前的最后一个(最高)键。
7).Map.Entry<K,V> firstEntry() 返回与此映射中的最小键关联的键 - 值映射,如果映射为空,则 null 。
8).Map.Entry<K,V> lastEntry() 返回与此映射中的最大键关联的键 - 值映射,如果映射为空,则 null 。
注意
Map集合下所有的实现类,去重|排序都是根据key实现的,与value无关
1).此实现不同步;
2).根据key做排序的前提必须先对key的数据做比较,需要涉及到比较器,具体使用内部还是外部根据构造器的参数决定;
3).空参数构造器,根据key的数据类型的内部比较器规则进行比较排序;
4).带参TreeMap(Comparator<? super K> comparator)构造器,根据参数传递的外部比较规则对key的数据进行比较排序;
5).去重 : 根据key的比较器实现去重与排序,key相同value会覆盖;
HashMap
基础知识
1).定义:基于哈希表的Map接口的实现,并允许null值和null键。根据key做无序去重
2).底层结构 : 哈希表 (jdk1.8之前: 数组+链表 jdk1.8及之后:数组+链表+红黑树)
3).特点 : 查询,增删效率较高;去重根据键值对的key实现;
4). 应用场景 : 存储键值对数据前提,查询,增删 效率较高的情况实现使用HashMap
5).新增方法 : 无新增方法
6). 遍历方式 : 3种
1)values()
2)keySet()
3)entrySet()
扩容及机制
1).初始容量 : 数组默认起始容量16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; | 根据构造器指定。
2).加载因子 : ==默认值0.75== static final float DEFAULT_LOAD_FACTOR = 0.75f;| 根据构造器指定
3).扩容阈值 : 扩容临界值 集合存储数据个数 > 数组的长度*加载因子实现扩容。
4).扩容机制 : 指的是哈希表结构中数组的扩容机制 newCap = oldCap << 1 默认扩容数组容量的2倍。
哈希表中存储键值对的个数 : size
源码分析 : 哈希表数据的存储过程。
==储存过程==
**1.**调用put方法添加键值对key,value数据
**2.**根据key计算hash值
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
**3.**调用putVal(hash,key,value)实现存储键值对数据
**4.**执行putVal方法,第一步就判断哈希表底层的节点数组是否null,或者底层数组的长度0,如果是证明这是第一次添加,底层没有数组或者没有带有容量的数组,先调用resize()方法实现创建新数组
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
**5.**根据key的hash值计算位桶索引int index = (n - 1) & hash,判断table[index]==null是否存在单向链表的首节点
if ((p = tab[i = (n - 1) & hash]) == null)
**6.**如果=null,证明不存在单向链表,直接创建新节点,存储这一次要添加的键值对数据,直接放在数组table[index]作为单向链表的首节点
tab[i] = newNode(hash, key, value, null); --> new Node<>(hash, key, value, next)
**7.**如果!=null,证明存在单向链表的首节点,遍历这个链表,判断链表中每一个节点的存储的key与这一次要添加的键值对的key比较是否相等,
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
如果相同value覆盖
V oldValue = e.value;
e.value = value;
return oldValue;
如果不相同创建新节点,挂在原单向链表的最后
p.next = newNode(hash, key, value, null);
**8.**如果以上的步骤中执行到了new Node()创建新节点放入哈希表中,数据个数+1,判断是否>扩容阈值threshold,如果满足,就调用resize方法实现扩容
if (++size > threshold)
resize();
注意
1).哈希表底层节点数组的长度为2的整数次幂。
2).当单向链表的节点个数>=8(static final int TREEIFY_THRESHOLD = 8;),并且同时哈希表的节点数组的容量>=64(static final int MIN_TREEIFY_CAPACITY = 64;),就把单向链表优化成红黑树;
但是如果数组的容量<64,就调用resize()实现数组的扩容。
3).使用HashMap存储数据,key的数据类型中要求重写hashCode与equals方法。
public class Class004_HashMap {
public static void main(String[] args) {
HashMap<String,Character> map = new HashMap<>();
map.put("yinwei",'z');
map.put("laoxue",'w');
map.put("lucy",'u');
map.put("lisa",'a');
System.out.println(map);
map.put("lisa",'s');
System.out.println(map);
map.remove("lisa");
System.out.println(map);
}
}
HashTable
与HashMap的区别
共同点 : 都是Map接口下的实现类,底层都是哈希表
不同点
**1.继承体系不同**
HashMap继承AbstractMap<K,V>
HashTable继承 Dictionary<K,V>
**2.对null值处理不同**
HashMap : 允许null值和null键。
Hashtable : 任何非null对象都可以用作键或值。
**3.同步问题|线程安全问题**
HashMap : 不同步的|线程不安全的
Hashtable : 同步的|线程安全的
**4.初始容量与扩容机制不同**
HashMap :初始容量 : 16 | 扩容机制 : 每次扩容原容量 的2倍
Hashtable:初始容量 : 11 | 扩容机制 : 每次扩容原容量的2倍+1 int newCapacity = (oldCapacity << 1) + 1;
**5.计算hash值与位桶索引index不同**
**HashMap :** int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
int index = (n - 1) & hash;
**Hashtable :** int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
6.注意:
如果不需要线程安全实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的高度并发实现,则建议使用ConcurrentHashMap代替Hashtable 。
如何使用线程安全的HashMap :
1.使用 Hashtable
2.Collections类 ->static <K,V>Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
3.juc包下的ConcurrentHashMap --> 推荐
Properties
基础知识
1、定义
Properties类表示一组持久的属性。
Properties可以保存到流中或从流中加载。
属性列表中的每个键及其对应的值都是一个字符串。
2、应用场景
从properties格式的配置文件中读取键值对数据
3、常用方法
1).String getProperty(String key) 在此属性列表中搜索具有指定键的属性。
2).void list(PrintStream out) 将此属性列表打印到指定的输出流。
3).void list(PrintWriter out) 将此属性列表打印到指定的输出流。
4).void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。
5).void load(Reader reader) 以简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
6).Object setProperty(String key, String value) 调用 Hashtable方法 put 。
实现步骤
1.定义xx.properties的配置文件
2.构建Properties类型的对象
3.调用load方法实现从指定流中加载
4.调用getProperty方法根据key获取value从文件中加载数据
public class Class001_Properties {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
pro.setProperty("嘻嘻","123");
pro.setProperty("哈哈","456");
System.out.println(pro);
//1)通过Properties对象从数据源文件中加载键值对数据(属性数据)
//把pro对象与流建立联系,从流中加载数据
//void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。
//pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("classname.properties"));
//根据key获取value,根据属性名获取属性值
//System.out.println(pro.getProperty("xixi"));;
//System.out.println(pro.getProperty("haha"));;
//System.out.println(pro.getProperty("hehe"));;
//2)把Properties存储的键值对数据写出到文件中-->了解
//pro对象与流建立联系,把键值对数据写出到目的地properties文件中
//void store(OutputStream out, String comments)
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("src/dest.properties"));
pro.store(os,"注释xixihaha");
os.flush();
os.close();
}
}
Collections
void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
void shuffle(List) //对List容器内的元素进行随机排列
void reverse(List) //对List容器内的元素进行逆续排列
void fill(List, Object) //用一个特定的对象重写整个List容器
int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
定义一个List集合存储Computer类型数据,要求根据电脑的价格做降序排序
如何处理HashMap线程不安全问题 :
1.Hashtable代替HashMap
2.使用Collections工具类的static <K,V>Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
3.juc高级并发编程包ConcurrentHashMap<K,V> --> 推荐
public class Class001_Collections {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(3,1,5,2,4);
System.out.println(list);
//void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
Collections.sort(list);
System.out.println(list);
//void shuffle(List) //对List容器内的元素进行随机排列
Collections.shuffle(list);
System.out.println(list);
//void reverse(List) //对List容器内的元素进行逆续排列
Collections.reverse(list);
System.out.println(list);
//int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
//先要求升序排序
Collections.sort(list);
System.out.println(Collections.binarySearch(list,14)); //-6 -插入点-1
ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
map.put("haha",123);
map.put("hehe",234);
map.put("haha",432);
System.out.println(map);
}
}