Map:存储双列数据,存储key-value键值对数据
HashMap:作为Map的主要实现类;线程不安全,效率高;可以存储null的key和value
LinkedHashMap:保证在遍历元素时可以按照添加的顺序遍历。
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁遍历的操作,此类执行效率高于HashMap
TreeMap:保证按照添加的key-value进行排序,实现排序遍历,此时考虑key的自然排序和定制排序,底层使用红黑树
hashtable:作为古老的实现类;线程安全,效率底;不能存储null的key和value
- Properties:常用来处理配置文件。key和value都是String类型
HashMap底层:数组+链表(jdk7及之前)
数组+链表+红黑树(jdk8)
HashSet底层使用的是HashMap,Set的值存储在Map的key位置,而value位置存储的是一个类对象的Object,无实际意义
HashMap结构理解
- HashMap中的key:无序的、不可重复的 ————> key所在的类要重写equals()和hashCode()
- HashMap中的value:无序的、可重复的 ————> value所在的类要重写equals()
- 一个键值对:key-value构成了一个Entry对象,Map中的Entry是无序的,不可重复复的
注意
- key所在的类要重写equals()和hashCode()
- value所在的类要重写equals()
- 当key比较一致的时候,新的value会替换旧的value
Map中定义的方法
增、删、改
- V put(K key, V value):将指定的key-value添加到(或修改)当前map对象中
- void putAll(Map m):将参数的m中所有的key-value添加到map中
- V remove(Object key):删除指定key的key-value,并返回value
- void clear():清空map中所有数据
元素查询的方法
- V get(Object key):返回key对应的value,无则返回null;
- boolean containsKey(Object key):是否包含指定key
- boolean containsValue(Object value):是否包含指定的value
- int size():返回Map中键值对的个数
- boolean isEmpty():判断map是否为空
- boolean equals(Object o):判断当前map与参数对象是否相等
元视图的操作方法
- Set keySet() 获取map中包含所有key的Set对象集合
- Collection values() 获取map中包含的value的Collection集合
- Set<Map.Entry<K,V>> entrySet() 获取map中所有key-value构成Entry的Set集合
HashMap
增删改
@Test
public void test1() {
HashMap<String,Integer> map = new HashMap<>();
//put(K key, V value)
map.put("小明",18);
map.put("小红",16);
map.put("小军",17);
map.put("小明",16); //第二次添加覆盖了原来的value值,相当于修改了数据
System.out.println(map);
HashMap<String,Integer> map1 = new HashMap<>();
map1.put("小强",19);
map1.put("小兰",15);
//putAll(Map m)
map.putAll(map1);
System.out.println(map);
//remove(Object key)
map.remove("小明");
System.out.println(map);
//clear()
map.clear();
System.out.println(map);
}
元素的查询方法
@Test
public void test2() {
HashMap<String,Integer> map = new HashMap<>();
map.put("小明",18);
map.put("小红",17);
map.put("小兰",17);
//V get(Object key)
Integer value = map.get("小明");
System.out.println(value);
//containsKey(Object key):是否包含指定key
boolean flag;
flag = map.containsKey("小红");
System.out.println(flag);
flag = map.containsKey("小娜");
System.out.println(flag);
//containsValue(Object value):是否包含指定的value
flag = map.containsValue(17);
System.out.println(flag);
flag = map.containsValue(19);
System.out.println(flag);
//size()
int size = map.size();
System.out.println(size);
//isEmpty():判断map是否为空
flag = map.isEmpty();
System.out.println(flag);
//equals(Object o):判断当前map与参数对象是否相等
HashMap<String,Integer> map1 = new HashMap<>();
map1.put("小明",18);
map1.put("小兰",17);
map1.put("小红",17);
flag = map.equals(map1);
System.out.println(flag);
}
元视图的操作方法,遍历map元素
@Test
public void test3() {
HashMap<String,Integer> map = new HashMap<>();
map.put("小明",18);
map.put("小红",17);
map.put("小兰",17);
//遍历所有的key
//Set<K> keySet() 获取map中包含所有key的Set对象集合
Set<String> keySet = map.keySet();
Iterator<String> keyIterator = keySet.iterator();
while (keyIterator.hasNext()) {
System.out.println(keyIterator.next());
}
//遍历所有的value
//Collection<V> values() 获取map中包含的value的Collection集合
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.println(value);
}
//遍历所有的key-value
//方式一:通过Set<Map.Entry<K,V>> entrySet() 获取map中所有key-value构成Entry的Set集合
Set<Map.Entry<String, Integer>> entries = map.entrySet();
Iterator<Map.Entry<String, Integer>> entryIterator = entries.iterator();
while (entryIterator.hasNext()) {
System.out.println(entryIterator.next());
}
//方式二:通过key,在map中查找value
Set<String> keySet1 = map.keySet();
Iterator<String> keyIterator1 = keySet1.iterator();
while (keyIterator1.hasNext()) {
String nextKey = keyIterator1.next();
System.out.println(nextKey + " --> " + map.get(nextKey));
}
}
HashMap实现原理
jdk7为例
(1)HashMap map = new HashMap();
- 实例化后,底层创建了长度为16的一维数组Entry[] table
(2)map.put(key1,value1);
首先调用key1所在类的hashCode()计算出key1的哈希值,此哈希值经过某种算法计算之后,得到在Entry[]数组中的存放位置。
如果此位置上没用数据,此时key1-value1 添加成功。————情况1
如果此位置上有一个或多个数据(以链表的形式进行存储),比较key1和已经存在的数据的哈希值:
- 如果key1的哈希值与已经存在的元素的key的哈希值不同,此时key1-value1添加成功 ————情况2
- 如果key1的哈希值与预警存在的某个元素的key哈希值相同,继续比较:调用key所在类的equals(),比较key值
- 如果equals()返回false:此时key1-value1添加成功。
- 如果equals()返回true:使用value1替换旧的value
补充
一个位置上存在多个元素,元素使用链表的方式进行存储
在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来
jdk8 相交于jdk7在底层实现方面的不同
new HashMap():底层没有立即创建一个长度为16的数组
jdk8底层的数组是:Node[],而非Entry[]
首次调用put()时,底层创建长度为16的Node[]数组
jdk7底层结构只有:数组+链表。jdk8底层结构:数组+链表+红黑树。
当数组的某个索引位置上的元素以链表的形式存储的数据 > 8且当前数组长度 > 64时,此索引位置上的所有数据改为红黑树存储
TreeMap
注意
TreeMap 按照key进行排序的,通过Java的比较器进行比较排序,所以key的类需要实现Comparable接口,并且存储的元素key都是同一个类的对象
排序
- 自然排序:key实现Comparable接口,重写compareTo()方法
- 定制排序:在实例化key的时候构造器传入自定义的比较器Comparator对象
自然排序
添加的元素的key所在的类需要实现Comparable接口,重写compareTo()方法
@Test
public void test1() {
//Person类中已经按照姓名从小到大,年龄从小到大的排序
TreeMap<Person,String> map = new TreeMap<>();
map.put(new Person("小明",18),"香蕉");
map.put(new Person("小红",16),"苹果");
map.put(new Person("小强",16),"猕猴桃");
map.put(new Person("小钢",17),"菠萝");
System.out.println(map);
}
定制排序
在创建Map对象的时候需要在构造器中传入自定义的比较器Comparator对象
//定制排序
@Test
public void test2() {
//先按照年龄从小到大排序,然后按照姓名从小到大排序
TreeMap<Person,String> map = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
int flag = o1.getAge() - o2.getAge();
if(flag != 0) {
return flag;
} else {
return o1.getName().compareTo(o2.getName());
}
}
});
map.put(new Person("小明",18),"香蕉");
map.put(new Person("小红",16),"苹果");
map.put(new Person("小强",16),"猕猴桃");
map.put(new Person("小钢",17),"菠萝");
System.out.println(map);
}
Properties
Properties:常用来处理配置文件。key和value都是String类型
使用Properties加载配置文件
@Test
public void test1() {
Properties properties = new Properties();
try(InputStream is = new FileInputStream("src/Map/jdbc.properties")) {
properties.load(is);
String name = properties.getProperty("name");
String age = properties.getProperty("age");
System.out.println(name + "-->" + age);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
jdbc.properties文件
name=mango
age=19