HashMap的EntrySet学习(HashMap的遍历)
EntrySet 和Map.Entry<K,V>
set的迭代器
map键值对的遍历学习
Map<Integer,Integer> map=new HashMap<>();
map.put(10,20);
map.put(20,45);
//获取键值对的set集合
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
//是set结合都可以使用iterator迭代器来遍历
Iterator<Map.Entry<Integer, Integer>> iterator = entries.iterator();
System.out.println("使用set的迭代器实现map遍历:");
while (iterator.hasNext()){
Map.Entry<Integer, Integer> next = iterator.next();
System.out.println(next.getKey()+" "+next.getValue());
}
System.out.println("set的foreach来实现遍历:");
for (Map.Entry<Integer, Integer> entry : entries) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
TreeMap使用学习
注意:treeMap的key的字符串的升序排序,本质属于按照首字符开始的ASSIC码的升序排序(0:48,A:65,a:97),但是可能出现长度长的字符串在短的字符串的前面。(针对咱们自定义比较器对key进行排序)
在按照key的排序基础上,再进行按照value进行排序,需要转换为entryset,将entryset装入list容器/或者封装为2维数组里,再进行按照value进行排序。
(注意1:按照value进行排序必须将map集合转换为entrySet进行排序
注意2:此时value的排序的优先级是最高的!!!)
显然如果想先按照value排序再按照key排序,要么需要重构treemap将key和value的位置相互互换;要么key和value可以封装为一个对象,对它使用PriorityQueue进行排序输出!!!
注意:set集合本身是不能进行排序的,Collections.sort(list容器,比较器)是给有序集合例如list来使用的(所以需要将entryset装到list容器里)
注意:实际PriorityQueue的排队功能更加强大一点
荣耀按照时间戳排序的学习
public class Main2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int size=scanner.nextInt();
Set<String> set=new HashSet<>();
// 首先去除相同的字符串
for(int i=0;i<size;i++){
set.add(scanner.next());
}
// 稳定性排序:确保按照ASSIC的字典排序
Map<String,Long> map=new TreeMap<>();
for(String s:set){
String[] sp=s.split("/");
for(int i=0;i<sp.length;i++){
if(isDate(sp[i])){
Date date=getDate(sp[i]);
long t=date.getTime();
map.put(s,t);
break;
}
}
}
ArrayList<Map.Entry<String, Long>> entries = new ArrayList<>(map.entrySet());
// 之后确保:按照时间戳排序,再按照长度进行排序
Collections.sort(entries, new Comparator<Map.Entry<String, Long>>() {
@Override
public int compare(Map.Entry<String, Long> o1, Map.Entry<String, Long> o2) {
if(!o1.getValue().equals(o2.getValue())){
return o1.getValue().compareTo(o2.getValue());
}else{
return Integer.compare(o1.getKey().length(),o2.getKey().length());
}
}
});
for(Map.Entry<String, Long> e:entries){
System.out.println(e.getKey());
}
}
public static boolean isDate(String s){
Date date=null;
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
date=sdf.parse(s);
} catch (ParseException e) {
// e.printStackTrace();
}
if(date==null){
return false;
}else{
return true;
}
}
public static Date getDate(String s){
Date date=null;
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
date=sdf.parse(s);
} catch (ParseException e) {
// e.printStackTrace();
}
return date;
}
}
考虑封装多个属性为一个类—实现Comprate接口&&compareTo方法,之后直接调用list集合进行排序
//希望实现按照时间,长度好和ASSIC码进行排序
public class DateString implements Comparable<DateString>{
public String source;
public Long date;
public int len;
DateString(String source,Long date){
this.source=source;
this.len=source.length();
this.date=date;
}
@Override
public int compareTo(DateString o) {
if(date.equals(o.date)){
if(len==o.len){
return source.compareTo(o.source);
}else{
return Integer.compare(len,o.len);
}
}else{
return Long.compare(date,o.date);
}
}
}
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int size=scanner.nextInt();
Set<String> set=new HashSet<>();
// 首先去除相同的字符串
for(int i=0;i<size;i++){
set.add(scanner.next());
}
List<DateString> list=new ArrayList<>();
for(String s:set){
String[] sp=s.split("/");
for(int i=0;i<sp.length;i++){
if(isDate(sp[i])){
long date = getDate(sp[i]);
list.add(new DateString(s,date));
break;
}
}
}
Collections.sort(list);
for (DateString dateString : list) {
System.out.println(dateString.source);
}
}
public static boolean isDate(String s){
Date date=null;
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
date=sdf.parse(s);
} catch (ParseException e) {
// e.printStackTrace();
}
if(date==null){
return false;
}else{
return true;
}
}
public static long getDate(String s){
Date date=null;
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
date=sdf.parse(s);
} catch (ParseException e) {
// e.printStackTrace();
}
return date.getTime();
}
}
SimpleDateformat学习
使用构造函数自定义需要解析的时间格式的模式情况,使用parse()方法解析具体的字符串
Date类的学习
注意date一般转换为long进行比较两个时间的大小,或者使用自带的after和before进行比较两个时间
LinkedHashMap(继承自HashMap)
指定顺序的构造函数(默认插入顺序;访问顺序-高级的LRU(最近最少使用))
访问顺序(每次都是在访问过的节点移动到双向链表的尾结点)
最近最少使用的原理
题:LRU缓存(最近最少使用)
力扣题:实现最近最少使用缓存
LRU类的成员变量:容量+当前size+假头he假尾(双向链表,维护最近使用关系)+map集合实现cache(数据 , 数据节点的位置信息)
双向链表需要自己创建:key,value,pre,next,构造函数
将最近使用的放在链表的头部,最少使用的都在链表的尾部。put和get都会引发将数据的最新的使用节点移动至链表的头部
class LRUCache {
private int capacity;
private int size;
private DLinkedNode head;//构建假头与假尾方便拼接与删除
private DLinkedNode tail;
private Map<Integer,DLinkedNode> cache;
//构造一个双向链表,存储的值是key和value
class DLinkedNode{
int key;
int value;
DLinkedNode pre;//前驱结点
DLinkedNode next;//后驱节点
public DLinkedNode(){}
public DLinkedNode(int key,int value){
this.key=key;
this.value=value;
}
}
//初始化缓存
public LRUCache(int capacity) {
this.capacity=capacity;
size=0;
head=new DLinkedNode();
tail=new DLinkedNode();
head.next=tail;
tail.pre=head;
cache=new HashMap<Integer,DLinkedNode>();
}
public int get(int key) {
if(!cache.containsKey(key)){
return -1;
}else{
DLinkedNode cur=cache.get(key);
int value=cur.value;//注意get操作也要更新
moveToHead(cur);
return value;
}
}
public void put(int key, int value) {
//如果不存在这个key,则加入
if(!cache.containsKey(key)){
size++;
DLinkedNode add=new DLinkedNode(key,value);
addToHead(add);
cache.put(key,add);
if(size>capacity){
DLinkedNode tail=deleteTail();
cache.remove(tail.key);
size--;
}
}else{//存在这个数就更新
DLinkedNode p=cache.get(key);
p.value=value;
cache.put(key,p);
moveToHead(p);
}
}
//删除一个节点
public void removeNode(DLinkedNode cur){
cur.pre.next=cur.next;
cur.next.pre=cur.pre;
}
//增加一个节点至头部(假头的后面)
public void addToHead(DLinkedNode cur){
DLinkedNode temp=head.next;
head.next=cur;
cur.pre=head;
cur.next=temp;
temp.pre=cur;
}
//将一个节点移动到头结点(删除原来位置的节点+加入头结点)
public void moveToHead(DLinkedNode cur){
removeNode(cur);
addToHead(cur);
}
//删除尾部节点(并返回删除的节点,用于删除缓存数据)
public DLinkedNode deleteTail(){
DLinkedNode t=tail.pre;//位于假头的前面
removeNode(t);
return t;
}
}
PriorityQueue(优先级队列)
不指定排序顺序,默认升序排列;利用Comparator接口实现
peek()和poll()都是针对队首元素(排序的队列)
add操作要按照定义的优先级插入,不一定是队尾
题: 前k个高频元素
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int len=nums.length;
Map<Integer,Integer> map=new HashMap<>();
for(int i=0;i<len;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
//按照数组的第二个元素升序排列(队列头部是最小的数:peek就是队列的头部)
PriorityQueue<int[]> proQueue=new PriorityQueue<int[]>(new Comparator<int[]>(){
public int compare(int[] o1,int[] o2){
return Integer.compare(o1[1],o2[1]);
}
});
Set<Map.Entry<Integer,Integer>> mapEntrySet=map.entrySet();
for(Map.Entry<Integer,Integer> set:mapEntrySet){
int value=set.getKey(),count=set.getValue();
if(proQueue.size()==k){
//当前仅当当前元素的数量多于队列的头部
if(count>proQueue.peek()[1]){//peek是队首元素
proQueue.poll();//将队首元素弹出
proQueue.add(new int[]{value,count});//在队列添加元素(需要按照定义的顺序插入)
}
}else{
proQueue.add(new int[]{value,count});
}
}
int[] res=new int[k];
int i=k-1;
while(i>=0){//每次弹出队首元素,倒序放在数组(最高频率的在数组的最前面)
res[i]=proQueue.poll()[0];
i--;
}
return res;
}
}
题:美团笔试题—先按照很高排序,身高相等按照名字字典排序
PriorityQueue版本+使用String的字典顺序比较两个字符串
public class Solution2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String numInf = scanner.nextLine();
int num=Integer.parseInt(numInf);
String heightInf=scanner.nextLine();
String nameInf=scanner.nextLine();
String[] heightInfArr=heightInf.split(" ");
int[] heightArr=new int[num];
for(int i=0;i<num;i++){
heightArr[i]=Integer.parseInt(heightInfArr[i]);
}
String[] nameArr=nameInf.split(" ");
//使用map进行封装
Map<String,Integer> map=new HashMap<>();
for(int i=0;i<num;i++){
map.put(nameArr[i],heightArr[i]);
}
Set<Map.Entry<String, Integer>> entries = map.entrySet();
PriorityQueue<String[]> pro=new PriorityQueue<String[]>(new Comparator<>() {
@Override
public int compare(String[] o1, String[] o2) {
int h1=Integer.parseInt(o1[1]);
int h2=Integer.parseInt(o2[1]);
if(h1==h2){
return o1[0].compareTo(o2[0]);//如果身高相等,按照字典排序名字(使用String本身的字典排序比较器)
}else{
return Integer.compare(h1,h2);//身高不相等先按照身高排序
}
}
});
for (Map.Entry<String, Integer> entry : entries) {
pro.offer(new String[]{entry.getKey(),entry.getValue()+""});
}
StringBuilder sb=new StringBuilder();
for(int i=0;i<num;i++){
sb.append(pro.poll()[0]);//注意PriorityQueue的poll()是从头部弹出数据的
if(i<num-1){
sb.append(" ");
}
}
System.out.println(sb.toString());
}
}
TreeMap版本(key本身按照字典排序)+自定义比较器按照值排序
public class Solution2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String numInf = scanner.nextLine();
int num=Integer.parseInt(numInf);
String heightInf=scanner.nextLine();
String nameInf=scanner.nextLine();
String[] heightInfArr=heightInf.split(" ");
int[] heightArr=new int[num];
for(int i=0;i<num;i++){
heightArr[i]=Integer.parseInt(heightInfArr[i]);
}
String[] nameArr=nameInf.split(" ");
//使用treemap对键按照字典排序,自定义比较器按照值进行排序
Map<String,Integer> map=new TreeMap<>();
for(int i=0;i<num;i++){
map.put(nameArr[i],heightArr[i]);
}
List<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());
Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue()-o2.getValue();
}
});//(o1,o2)->o1.getValue()-o2.getValue(),使用lambda表达式
StringBuilder sb=new StringBuilder();
int count=0;
for (Map.Entry<String, Integer> entry : entries) {
sb.append(entry.getKey());
count++;
if(count<num){
sb.append(" ");
}
}
System.out.println(sb.toString());
}
}