0# Lesson18_Map_Set_Collections
回顾
Iterable ---- Iterator
Collection
List 有序、可重复
ArrayList 数组
LinkedList 链表
Set
- Set继承与Collection方法上与List一样
- 底层是Map,认为你的元素就是作为底层Map的键
- 无序的,不能重复
HashSet
- 底层就是HashMap,实际使用的就是HashMap的键
- 键可以为null,只能有一个
- 元素不重复,可以包含null值。
- 不能排序
TreeSet
- 底层就是TreeMap,实际使用的就是TreeMap的键
- 不能有null值
- 必须要有排序
- 元素不重复,要求排序的,不能包含null值的。
Map
-
键值对
-
顶级接口
-
键是唯一的。值可以重复
-
键可以为null的。值也是可以为null。键只能有一个null。
-
entry将键值作为整体保存的
@Test public void test05() { Map<Integer,String> map = new HashMap<>(); //存入一个键值对 map.put(1,"abc"); map.put(2,"bcd"); map.put(3,"ljl"); map.put(4,"luiliu"); map.put(5,"iuwiy"); //如果存入的键已经存在,会将原来的值覆盖掉 map.put(1,"ytg"); //情况键值 //map.clear(); //判断是否包含某个键 boolean b = map.containsKey(2); System.out.println(b); System.out.println("----------------"); //判断是否包含某个值 boolean abc = map.containsValue("abc"); System.out.println(abc); System.out.println("----------------"); //通过给定的键获取对应的值 String s = map.get(1); System.out.println(s); System.out.println("----------------"); //判断是否为空 map.isEmpty(); //通过键删除一对元素 map.remove(1); //返回所有的值 Collection<String> values = map.values(); values.forEach(System.out::println); System.out.println("----------------"); //返回entry类型的set集合 Set<Map.Entry<Integer, String>> entries = map.entrySet(); for (Map.Entry<Integer, String> entry : entries) { System.out.println(entry); } System.out.println("----------------"); //返回所有的键,存入set集合 Set<Integer> integers = map.keySet(); Iterator<Integer> iterator = integers.iterator(); while (iterator.hasNext()) { Integer next = iterator.next(); System.out.println(next+"="+map.get(next)); } }
HashMap
- Map的一个子类
- 数据结构是hash表结构的
- hash表=数组+链表(红黑树=自平衡的二叉树=分支)
- 数组你认为的那个数组
- 二叉树,以链表为基础实现的。第一个数作为根,后面的数依次比较大小,如果大于跟,存到右边,如果小,左边,hashcode
-
- 二叉树的缺点就是如果跟定义不合适失去平衡,某一条分支太重
- 为了解决二叉树是缺点,我们1.8以后不再二叉树(单边多余7个,总数超过18个),将二叉树转为红黑树
- 红黑树的特点:
- 相比二叉树有自平衡(重新定义根节点)
- 左旋:往左边转
- 右旋:往右边转
- 所有的节点,要么是黑的,要么是红的
- 根节点一定是黑的
- 所有的尾节点一定是黑的
- 相比二叉树有自平衡(重新定义根节点)
- 先得到键的hash值,通过hash值得到数组中的下标,存入进去
- 如果得到的两个键的hash的下标一样,再调用equals比较
- 如果equals比较相等,认为键相等,值覆盖
- 如果equals比较不想等。通过二叉树的形式连在后面
- 1.8以前,依次往后连接,之后,往前连接,当链表长度超过8个,转为二叉树
- 当同一个下标下的二叉树到了转换的条件(一边比另一半多6个或者整体到64个),就转为红黑树了
- 源码分析
- DEFAULT_INITIAL_CAPACITY = 1 << 4;初始容量16个
- MAXIMUM_CAPACITY = 1 << 30;最大容量
- DEFAULT_LOAD_FACTOR = 0.75f;加载因子,可以大于1的
- 扩容的时候直接增加一倍,容量始终是2的幂方
- TREEIFY_THRESHOLD = 8;链表转二叉树
- UNTREEIFY_THRESHOLD = 6;二叉树失重
- MIN_TREEIFY_CAPACITY = 64;重新转为链表的个数
@Test
public void test02(){
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put("1","aa");
hashMap.put("2","bb");
hashMap.put("3","cc");
hashMap.put("4","dd");
hashMap.put("5","ee");
//map.clear();
//是否元素为空
boolean empty = hashMap.isEmpty();
System.out.println("是否元素为空 = " + empty);
//长度
int size = hashMap.size();
System.out.println("长度 = " + size);
//修改
hashMap.replace("2","hh");
//是否包含值
String d = hashMap.get("d");
System.out.println("d = " + d);
//删除
hashMap.remove("3");
//键
Set<String> strings = hashMap.keySet();
System.out.println("键 = " + strings);
//键值
Set<Map.Entry<String, String>> entries = hashMap.entrySet();
System.out.println("键值 = " + entries);
//值
Collection<String> values = hashMap.values();
System.out.println("值 = " + values);
//迭代
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
- hashMap线程不安全
- 如果涉及到多线程的操作,要使用HashMap的话,可以使用它的代替品
- ConcurrentHashMap:线程安全的,扩容时
- HashTable:方法都是线程安全的
TreeMap
- 键不能为空
- 所有的元素必须是可以排序的
- 要么键的对象类型实现了自然排序,要么构造方法中传入比较器
- 底层使用的是红黑树
- 效率比HashMap低
- 如果需要键有序排列的,使用
- 一般使用外部的比较器
- 外部传入的比较器的优先级高于自然比较的
@Test
public void test06() {
TreeMap<Users,String> map = new TreeMap<>((u1,u2) ->
u1.getId()-u2.getId()
);
map.put(new Users(1,"张三","zhangsan"),"张三");
map.put(new Users(4,"赵六","zhaoliu"),"赵六");
map.put(new Users(2,"李四","lisi"),"李四");
map.put(new Users(5,"孙七","sunqi"),"孙七");
map.put(new Users(3,"王五","wangwu"),"王五");
Set<Map.Entry<Users, String>> entries = map.entrySet();
Iterator<Map.Entry<Users, String>> iterator = entries.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
- 实现SortedMap
Collections
- 类似于Arrays
- 操作集合的工具类
- 都是静态方法。直接用
@Test
public void test07() {
HashSet<Users> set = new HashSet<>();
//针对元素拥有自然排序的List集合进行排序
Collections.sort(new LinkedList<String>());
//对List集合,根据给定的比较器进行排序
Collections.sort(new ArrayList<String>(),(s1,s2) -> s2.length()-s1.length());
//返回一个空的List集合
List emptyList = Collections.EMPTY_LIST;
//返回一个空的Set集合
Set emptySet = Collections.EMPTY_SET;
//返回一个空的Map集合
Map emptyMap = Collections.EMPTY_MAP;
Collection<String> collection = new HashSet<>();
//添加多个元素到参数集合中
Collections.addAll(collection,"abc","bcd");
ArrayList<Integer> list = new ArrayList<>();
//二分查找,从参数集合中查找参数给定的元素,返回下标
int i = Collections.binarySearch(list, 3);
//将元素全部填充到集合中
Collections.fill(list,2);
//返回一个String类型的只有参数一个元素的List集合
Collections.singletonList("abc");
//返回一个String类型的只有参数一个元素的Set集合
Collections.singleton("abc");
}
Propertry
/**
* @Author: 邪灵
* @Date: 2020/11/24 14:34
* @Description:
* @version: 1.0
*/
public class Demo {
@Test
public void test01() throws IOException {
Properties properties = new Properties();
//存入一条记录,键值对,都是String类型
properties.setProperty("key","values");
//修改一条记录,如果没有对应的键,则无反应
properties.replace("key1","values1");
//存入一条记录,同setProperty方法
properties.put("url","jdbc:mysql:///test");
properties.put("name","root");
properties.put("pass","root");
//通过键获取值,与put方法相对
properties.get("key");
//通过键获取值,等同于get方法,与getProperty方法对应
properties.getProperty("key");
//判断是否包含某个键
properties.containsKey("key");
//判断是否包含某个值
properties.containsValue("values");
//判断是否包含某个值,同上
properties.contains("values");
//获取一个枚举器,包含都是值,没有键
Enumeration<Object> elements = properties.elements();
System.out.println("---------枚举器遍历值----------");
while (elements.hasMoreElements()) {
//通过枚举器遍历所有的值
System.out.println(elements.nextElement());
}
//获取entry类型的set集合,用于遍历迭代
Set<Map.Entry<Object, Object>> entries = properties.entrySet();
System.out.println("---------entry遍历---------");
for (Map.Entry<Object, Object> entry : entries) {
System.out.println(entry);
}
//获取所有的值,同keyset方法
properties.keys();
//通过流的方式加载文件中的数据到集合中
properties.load(new FileInputStream("file"));
//获取所有的键,再通过键取到对应的值
Set<Object> objects = properties.keySet();
System.out.println("---------keyset遍历---------");
for (Object object : objects) {
System.out.println(object+"=="+properties.get(object));
}
}
}
【源码任务】
1.证明HashMap的容量是2的幂,即使是通过构造方法人为指定了容量,最后也会是2的幂。
2.证明Tree结构比较元素大小时,优先使用的Comparator的规则,而不是Comparable的规则。