Day17
一、初识Map
Map不能直接遍历-没有获取迭代器的功能。
clear()-清空
cotainsKey(),containsValue()-是否包含键/值,返回布尔值
entrySet() - 返回此地图中包含的映射的Set集合
get(key)-通过键获取值
二、HashMap
知识点:HashMap的使用
HashMap<String,Integer> map = new HashMap<>();
map.put(key,value) - 如果没有该映射则添加,此时返回的是null;如果有key就替换value,返回被替换的值。(注意:返回的值是包装类)
map.replace(k,v)-替换,如果有key就替换value返回被替换的值,没有key就返回null。
map.replace(k,oV,nV) - 通过key+value替换。
HashMap newMap = new HashMap<>();
map.putAll(newMap);-将newMap中的所有映射添加到map集合中。
map.putIfAbsent(k,v)-如果集合中有此key,则返回value,没有则添加并返回null。
map.get(k)-通过键返回值,如果不存在则返回null。
map.getOrDefault(k,v)-通过键返回值,如果不存在则返回默认值。
map.containsKey, map.containsValue, map.isEmpty和map中功能一致,均返回布尔值。
map.remove(k) - 根据key删除元素,返回被删除的value。
map.remove(k, v) - 根据key和value删除元素,成功返回true,失败返回false。
map.size() - 获取元素个数。
map.values() - 后去map集合中所有的value,返回的是value类型的集合。
map.keySet() - 用于遍历集合,获取map集合中所有的key,并返回一个set集合。
遍历思路1:遍历思路:keySet()将Map中所有的key获取出,放在Set集合中,遍历Set集合依次获取key,利用map.get(key)获取对应的value。
Set<String> keySet=map.keySet(); for(String key:keySet){ Integer value = map.get(key);//注意这里是从HashMap中获取的值。 System.out.print(key + "---"+value); }
entrySet() - 用于遍历集合,将map集合中的所有映射关系获取出,以Entry<k,v>的类型(映射关系)放在Set中。
遍历思路2:entrySet()将Map中所有的映射关系对象获取出,放在Set集合中,遍历Set集合依次遍历出映射关系对象,映射关系对象中包含了key和value
Set<Entry<String Integer>> entrySet = map.entrySet(); for(Entry<String,Integer> entry: entrySet){ String key = entry.getKey(); Integer value = entry.getValue();//注意这里是从集合entrySet中获取的元素,每个元素是一个Map.Entry<k,v>对象,再获取键和值。 System.out.println(key + " -- " + value); }
知识点:HashMap的特点
特点:无序 且 key去重(唯一)
知识点:HashMap的面试题
需求:给HashMap的value排序
思路:HashMap 获取映射关系对象的Set集合 -> ArrayList对象 -> list.sort(外置比较器)
public static void main(String[] args) { HashMap<String,Integer> map = new HashMap<>; map.put("生希", 28); map.put("名空", 23); map.put("菜丽", 29); map.put("桐光", 21); map.put("小康", 21); map.put("岛玲", 28); //获取映射关系对象的集合 Set<Entry<String,Integer>> entrySet = map.entrySet(); //将Set集合转换为ArraryList集合 ArrayList<Entry<String,Integer>> list = new ArrayList<>(entrySet);//调用了ArrayList类的一个有参构造,接受一个 Collection 类型的参数,用于将该集合中的元素添加到新创建的 ArrayList 中 //排序 list.sort(new Comparator<Entry<String,Integer>>() { @Override public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { return o1.getValue() - o2.getValue(); } }); for (Entry<String, Integer> entry : list) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key + "---" + value); System.out.println(entry); } }
三、LinkedHashMap,HashTable,ConcurrenthashMap
使用
方法和HashMap一致。
特点
LinkedHashMap的特点:
继承关系:class LinkedHashMap<K,V> extends HashMap
注意:LinkedHashMap在HashMap的基础上添加了双向链表
特点:有序且Key去重
Hashtable的特点:
特点:无序且key去重 + 线程安全(方法上加锁)(目前已弃用)
ConcurrentHashMap的特点:
特点:无序且key去重 + 线程安全(局部加锁)
比较
知识点:HashMap vs LinkedHashMap vs Hashtable vs ConcurrentHashMap
特点的区别:
HashMap:无序且去重
LinkedHashMap:有序且去重
Hashtable:无序且去重 + 线程安全(方法上加锁,已弃用)
ConcurrentHashMap:无序且去重 + 线程安全(局部加锁+CAS,效率更高)存储null键null值的区别:
HashMap:ok
LinkedHashMap:ok
Hashtable:no
ConcurrentHashMap:no
四、TreeMap
使用
和HashMap使用一致。
特点
特点:针对于key进行自然排序
内置比较器
public static void main(String[] args) { TreeMap<Student, String> map = new TreeMap<>(); map.put(new Student("桐光", '女', 30, "2402", "004"),"跳舞"); map.put(new Student("岛玲", '女', 18, "2402", "005"),"品茗"); map.put(new Student("井步", '女', 19, "2402", "006"),"对弈"); Set<Entry<Student,String>> entrySet = map.entrySet(); for (Entry<Student, String> entry : entrySet) { System.out.println(entry); } }
package com.qf.treemap_class; public class Student implements Comparable<Student>{ private String name; private char sex; private int age; private String classId; private String id; public Student() { } public Student(String classId, String id) { this.classId = classId; this.id = id; } public Student(String name, char sex, int age, String classId, String id) { this.name = name; this.sex = sex; this.age = age; this.classId = classId; this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getClassId() { return classId; } public void setClassId(String classId) { this.classId = classId; } public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public boolean equals(Object obj) { if(this == obj){ return true; } if(obj instanceof Student){ Student stu = (Student) obj; if(classId.equals(stu.classId) && id.equals(stu.id)){ return true; } } return false; } @Override public String toString() { return name + "\t" + sex + "\t" + age + "\t" + classId + "\t" + id; } //排序规则:按照年龄排序 @Override public int compareTo(Student o) { return this.age - o.age; } }
外置比较器
public static void main(String[] args) { TreeMap<Student, String> map = new TreeMap<>(new Comparator<Student>() { //排序规则:按照名字长度排序,名字长度一致按照年龄排序 @Override public int compare(Student o1, Student o2) { if(o1.equals(o2)){ return 0; } int nameLen1 = o1.getName().length(); int nameLen2 = o2.getName().length(); if(nameLen1 != nameLen2){ return nameLen1 - nameLen2; } int age1 = o1.getAge(); int age2 = o2.getAge(); if(age1 != age2){ return age1 - age2; } return 1; } }); map.put(new Student("桐光", '女', 30, "2402", "004"),"跳舞"); map.put(new Student("岛玲", '女', 18, "2402", "005"),"品茗"); map.put(new Student("井步", '女', 19, "2402", "006"),"对弈"); Set<Entry<Student,String>> entrySet = map.entrySet(); for (Entry<Student, String> entry : entrySet) { System.out.println(entry); } }
五、Properties
知识点:Properties
public static void main(String[] args) throws IOException { //配置文件对象 Properties properties = new Properties(); //将配置文件加载到对象中 properties.load(Test01.class.getClassLoader().getResourceAsStream("DBConfig.properties")); //获取配置文件里的数据 String username = properties.getProperty("username"); String password = properties.getProperty("password"); System.out.println(username + " -- " + password); }
六、Collections集合工具类
public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); //批量添加 Collections.addAll(list, 5,8,3,4,1,2,7,9,6); //排序 -- 内置比较器(按照元素所属类的排序规则) Collections.sort(list); //注意:查找之前必须先排序 int index = Collections.binarySearch(list, 3); System.out.println("获取元素的下标:" + index); //排序 -- 外置比较器 Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2-o1; } }); Integer max = Collections.max(list); System.out.println("最大值:" + max); Integer min = Collections.min(list); System.out.println("最小值:" + min); //替换所有元素 Collections.fill(list, 888); //获取线程安全的List集合 List<Integer> synchronizedList = Collections.synchronizedList(list); //[888, 888, 888, 888, 888, 888, 888, 888, 888] System.out.println(Arrays.toString(synchronizedList.toArray())); }
七、EnumMap 与EnumSet
//信号灯 public enum Signal { RED, YELLOW, GREEN; }
知识点:EnumSet – 可以存储枚举的Set集合
public static void main(String[] args) { //Set集合中存储了Signal的对象 EnumSet<Signal> set = EnumSet.allOf(Signal.class); set.remove(Signal.RED); Iterator<Signal> it = set.iterator(); while(it.hasNext()){ Signal next = it.next(); System.out.println(next); } }
知识点:EnumMap – 可以存储枚举的Map集合
注意:将枚举对象存储在key的位置
public class Test01 { /** * 知识点:EnumMap -- 可以存储枚举的Map集合 * 注意:将枚举对象存储在key的位置 */ public static void main(String[] args) { EnumMap<Signal,String> map = new EnumMap<>(Signal.class); map.put(Signal.RED, "红灯"); map.put(Signal.YELLOW, "黄灯"); map.put(Signal.GREEN, "绿灯"); Set<Entry<Signal,String>> set = map.entrySet(); for(Entry<Signal,String> entry:set){ System.out.println(entry); System.out.println(entry.getKey()+"---"+entry.getValue()); } }
解读:
EnumSet:由于枚举类型的元素是有限的且连续的,因此可以使用位向量来高效表示集合中的元素。这种实现方式使得
EnumSet
在性能和内存消耗方面都非常高效。因此,设计者认为直接提供静态工厂方法来创建EnumSet
对象更加简洁和高效。
EnumSet.of(o1, o2, o3, ...)
:创建包含指定枚举常量的 EnumSet 实例。
EnumSet.allOf(O.class)
:创建一个包含指定枚举类型的所有枚举常量的 EnumSet 实例。
EnumSet.noneOf(O.class)
:创建一个空的 EnumSet 实例。
EnumSet.range(o1, o3)
:创建一个从 o1 到 o3(包括 o1 和 o3)范围内的 EnumSet 实例。EnumMap:枚举类型作为键时,并不像枚举类型作为集合元素那样具有连续性和有限性。因此,
EnumMap
的实现使用了数组来存储映射关系,因此可以直接提供公共的构造方法来创建EnumMap
对象。
八、ArrayList底层源码
场景:
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); //添加数据 list.add("宇"); list.add("蒲"); list.add("小康"); }
源码:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { //外部操作数(记录添加和删除的次数) protected transient int modCount = 0;//3 }
public class ArrayList<E> extends AbstractList<E> implements List<E>{ //默认容量 private static final int DEFAULT_CAPACITY = 10; //空内容的数组 private static final Object[] EMPTY_ELEMENTDATA = {}; //默认容量的空内容的数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //最大的容器容量 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //数据容器 - [宇,蒲,小康,null,null,null,null,null,null,null] transient Object[] elementData; //元素个数 private int size;//3 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //initialCapacity - public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } //e - 小康 public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } //minCapacity - 1 private void ensureCapacityInternal(int minCapacity) { //使用无参构造创建ArrayList对象,第一次添加数据时进入的判断 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //minCapacity = Math.max(10,1); --> minCapacity = 10 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } //minCapacity - 11 private void ensureExplicitCapacity(int minCapacity) { modCount++; // 有溢出意识的代码 if (minCapacity - elementData.length > 0) grow(minCapacity);//扩容 } //minCapacity - 11 private void grow(int minCapacity) { // oldCapacity - 10 int oldCapacity = elementData.length; // newCapacity = 15 int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容机制:1.5倍 if (newCapacity - minCapacity < 0) //newCapacity - 10 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 扩容 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE; } }
几个面试题:
ArrayList底层数据结构是什么?
Object类型的一维数组
ArrayList默认初始化长度是多少?
10
ArrayList如何减少容器的伸缩性?
使用有参构造定义容量
ArrayList数组最大容量是多少?
Integer.MAX_VALUE-8
ArrayList数组最大容量为什么是Integer.MAX_VALUE-8?或 ArrayList数组最大容量为什么要减8?
减8是为了腾出空间存放数组的头部信息
ArrayList的扩容机制是什么?
是原来数组长度的1.5倍
九、LinkedList底层源码
场景:
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); //添加数据 list.add("宇"); list.add("蒲"); list.add("小康"); }
源码:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { //外部操作数 protected transient int modCount = 0; }
public abstract class AbstractSequentialList<E> extends AbstractList<E> { }
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>{ //元素个数 transient int size = 0;//0 //第一个节点 transient Node<E> first;//null //最后一个节点 transient Node<E> last;//null public LinkedList() { } public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; } //节点类 private static class Node<E> { E item;//元素 Node<E> next;//下一个节点 Node<E> prev;//上一个节点 Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } }
几个面试题:
LinkedList的底层数据结构是什么?
双向链表
ArrayList 和 LinkedList效率的区别?
ArrayList:一维数组
LinkedList:双向链表
添加功能 - 不扩容:ArrayList快
添加功能 - 扩容:LinkedList快
删除功能:LinkedList快
修改功能:ArrayList快
查询功能:ArrayList快
一般项目中使用ArrayList居多,因为业务流程里查询业务是最多的。
练习:
1.理解LinkedList是如何删除元素(底层)
E unlink(Node<E> x) { final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
2.掌握双向链表和单向链表的代码
/** 双向链表有first节点和last节点 */ void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++;
/** 单向链表中只有first节点或head节点 */ void linkLast(E e) { final Node<E> newNode = new Node<>(e); if (first == null) { first = newNode; } else { final Node<E> current = first; while (current.next != null) { current = current.next; } current.next = newNode; } size++; modCount++; }