Map接口
键值对<key,value>
Interface Map<K,V>
-
-
参数类型
K
- 由此地图维护的键的类型V
- 映射值的类型
-
public class TestMap {
public static void main(String[] args) {
/*
* 增加:put(K key, V value)
* 删除:clear();remove(Object key);
* 修改:
* 查看:entrySet();get(Object key);keySet();values();size()
* 判断:containsKey(Object key);containsValue(Object value);equals(Object o);isEmpty()
* */
//创建一个Map集合:唯一,无序
Map<String,Integer> map=new HashMap<>();
System.out.println(map.put("sky", 502));//null;put的返回值是v,即这里的integer
map.put("lsj",426);
map.put("yhm",456);
map.put("zrp",1715110092);
System.out.println(map.put("sky", 374));//502
System.out.println(map.size());//4
System.out.println(map);//{sky=374, lsj=426, zrp=1715110092, yhm=456}
/*map.clear();清空
System.out.println(map.size());//0
System.out.println(map);//{}*/
/*map.remove("sky");移除
System.out.println(map.size());//3
System.out.println(map);//{lsj=426, zrp=1715110092, yhm=456}*/
System.out.println(map.containsKey("swy"));//false
System.out.println(map.containsValue(426));//true
Map<String,Integer> map2=new HashMap<>();
map2.put("sky", 502);//null;put的返回值是v,即这里的integer
map2.put("lsj",426);
map2.put("yhm",456);
map2.put("zrp",1715110092);
map2.put("sky", 374);
System.out.println(map==map2);//false
System.out.println(map.equals(map2));//equals进行了重写比较的是集合中值,内容是否一样;true
System.out.println(map.isEmpty());//判断是否为空;false
System.out.println(map.get("sky"));//374
//遍历:
Set<String> set = map.keySet();//keySet()对集合中的key进行遍历查看
System.out.println("===============");
for(String s:set){
System.out.println(s);
}
System.out.println(map.keySet());//[sky, lsj, zrp, yhm]
//获取value的两种方式
Collection<Integer> values = map.values();//values()对集合中的value进行查看
System.out.println("===============");
for(Integer i:values){
System.out.println(i);
}
System.out.println(map.values());//[374, 426, 1715110092, 456]
//不用values()方法对value进行遍历
Set<String> set2 = map.keySet();
for(String s:set2){
System.out.println(map.get(s));
}
System.out.println("===============");
Set<Map.Entry<String, Integer>> entries = map.entrySet();//返回set集合
//Entry是Map接口里的一个内部接口
for(Map.Entry<String, Integer> e:entries){
System.out.println(e.getKey()+"-----"+e.getValue());
}
}
}
上面HashMap换成Hashtable,输出结果不变
HashMap实现类
特点:唯一,无序;不允许k的值重复
HashMap与Hashtable区别:
Map<String,Integer> map3=new Hashtable<>();
//map3.put(null,1010);//Exception in thread "main" java.lang.NullPointerException
//对于hashtable来说,key不可以是null值,存了会报错
Map<String,Integer> map4=new HashMap<>();
System.out.println(map4.put(null, 1010));//null
System.out.println(map4.put(null, 2020));//可以存放多个空
System.out.println(map4);//{null=2020}
LinkedHashMap实现类
特点:有序,唯一
LinkedHashMap<String,Integer> lmap=new LinkedHashMap<>();
lmap.put("asky",502);
lmap.put("bsky",502);
lmap.put("csky",502);
lmap.put("sky",502);
System.out.println(lmap);//{asky=502, bsky=502, csky=502, sky=502}
TreeMap实现类
1、key的类型为String类型
public class TestTreeMap {
public static void main(String[] args) {
Map<String,Integer> map=new TreeMap<>();
map.put("bsky", 502);
map.put("alsj",426);
map.put("byhm",456);
map.put("czrp",1715110092);
map.put("dsky", 374);
System.out.println(map.size());//5
System.out.println(map);//{alsj=426, bsky=502, byhm=456, czrp=1715110092, dsky=374}
//String底层已经实现了比较器
}
}
2、key的类型是一个自定义的类型
(1)内部比较器
(2)外部比较器
Map<Student,Integer> m1=new TreeMap<>();
m1.put(new Student(19,168.3,"flili"),103);
m1.put(new Student(18,168.3,"blili"),123);
m1.put(new Student(11,168.3,"clili"),1023);
m1.put(new Student(17,168.3,"dlili"),111);
m1.put(new Student(16,168.3,"alili"),167);
m1.put(new Student(20,168.3,"glili"),88);
System.out.println(m1);//按自己定义的比较器进行排序
System.out.println(m1.size());//6
Map<Student,Integer> m1=new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge()-o2.getAge();
}
});//匿名内部类的外部比较器
HashMap的简单原理
JDK1.7版本
提出问题:
public class OriHashMap {
public static void main(String[] args) {
//创建hashmap对象:存储的是双列数据,键值对key-value
HashMap<Integer,String> hm=new HashMap<>();
System.out.println(hm.put(12, "sky"));//null
System.out.println(hm.put(7, "feifei"));//null
System.out.println(hm.put(19, "lulu"));//null
System.out.println(hm.put(12, "mingming"));//sky
//这个12是sky的12还是mingming的12?
System.out.println(hm.put(2, "yhm"));//null
System.out.println(hm.size());//4
System.out.println(hm);//{2=yhm, 19=lulu, 7=feifei, 12=mingming}
}
}
答:12是sky的12
七上八下:jdk7链表头插法;jdk8尾插法
jdk1.7版源码
//1.hashmap的k,v值,在创建对象时确定
//hashmap的父类AbstractMap已经实现了Map接口,但源码中又单独实现了Map,这是多余的,错的,arraylist同理
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
//重要属性
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16,一会儿赋值给数组的长度
static final int MAXIMUM_CAPACITY = 1 << 30;//定义了一个很大很大的数
static final float DEFAULT_LOAD_FACTOR = 0.75f;//定义了一个值:0.75 装填因子,负载因子,加载因子
transient Entry<K,V>[] table;//底层主数组
transient int size;//集合中添加元素的数量
int threshold;//定义了变量,没赋值默认为0-----》数组扩容的边界值
final float loadFactor;//用来接收装填因子,负载因子,加载因子
put原理
put方法源码:
数组扩容原理
数组扩容的边界值是指?size超过12就扩容?
不是,是要size超过12且目标位置不为空才会扩容
经典面试题
1、装填因子,负载因子,加载因子为什么是0.75?
装填因子设置为1:空间利用率得到了很大的满足,很容易碰撞,产生链表------》查询效率低
装填因子设置为0.5:碰撞的概率第,扩容,产生链表的几率低------》查询效率高;空间利用率太低了
所以在0.5-1中取了一个中间值0.75
2、主数组的长度为什么必须为2^n?
HashSet底层原理
public class HashSet<E>{
//重要属性:
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
//构造器
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
}
TreeMap底层源码
二叉树就是红黑树
public class TreeMap<K,V>{
//重要属性:
//外部比较器
private final Comparator<? super K> comparator;
//树的根节点
private transient Entry<K,V> root;
//集合中元素的数量
private transient int size = 0;
//空构造器
public TreeMap() {
comparator = null;//如果使用空构造器,那么底层就不使用外部比较器
}
//有参构造器
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;//如果使用了有参构造器,那么相当于指定了外部比较器
}
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
}
public V put(K key, V value) {//K,V的类型在创建对象时确定
//如果放入的时第一对元素,那么t的值为null
Entry<K,V> t = root;//放入第二个元素时,root不为空,不走if
//如果放入的时第一个元素的话,走入这个if中
if (t == null) {
//自己与自己比较
compare(key, key); // type (and possibly null) check
//根节点确定为root
root = new Entry<>(key, value, null);
//size变成1
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
//将外部比较器赋值给cpr:
Comparator<? super K> cpr = comparator;
//cpr不等于null意味着创建对象时调用了有参构造器,指定了外部比较器
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);//将元素的key值做比较
//cmp返回int类型数据
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else//cmp==0;
//如果key的值一样,那么新的value替换老的value,但是key不变,key唯一
return t.setValue(value);
} while (t != null);
}//cpr等于null意味着创建对象时调用了空构造器,没有指定外部比较器,使用内部比较器
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;//size加一操作
modCount++;
return null;
}
}
TreeSet源码
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable{
//重要属性:
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
//调用空构造器时底层创建了一个TreeMap
public TreeSet() {
this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
//实际上时treemap的put方法
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
}
Collections工具类
public class TestCols {
public static void main(String[] args) {
//collections不支持创建对象,因为构造器私有化
//属性和方法通过类名调用
//常用方法:addAll
ArrayList<String> list=new ArrayList<>();
list.add("cc");
list.add("aa");
list.add("bb");
Collections.addAll(list,"dd","ff");
Collections.addAll(list,new String[]{"sky","lsj","yhm"});
System.out.println(list);
//sort升序排列
Collections.sort(list);
System.out.println(list);
//binarySearch:前提,集合内容有序
int i=Collections.binarySearch(list,"cc");
System.out.println(i);
//copy:把list2里面的内容替换到list里面
ArrayList<String> list2=new ArrayList<>();
Collections.addAll(list2,"tt","ss");
Collections.copy(list,list2);
System.out.println("============");
System.out.println(list);
System.out.println(list2);
//fill:填充,把list2里面的所有内容填充为yy
Collections.fill(list2,"yy");
System.out.println(list2);
}
}