算法 | 链表的应用,缓存失效算法

简介: 算法 | 链表的应用,缓存失效算法

先进先出策略(FIFO,First In First Out)


这种策略的实现是最简单的了,我们利用链表的特性,只要将需要缓存的数据依次添加到链表中,当空间不够时,将链表末尾的数据删除即可(如果我们对队列比较熟悉的话,可以发现这种策略用队列进行实现是最方便的)

实现代码如下:

package com.study.spring.transaction.cache;
import lombok.AllArgsConstructor;
import java.security.InvalidParameterException;
/**
 * @author dmz
 * @date Create in 20:57 2019/8/10
 */
public class Main {
    public static void main(String[] args) {
        link link = new link(4);
        link.add(1);
        link.add(2);
        link.add(3);
        link.add(4);
        link.add(5);
        link.add(6);
        link.add(7);
        link.sout();
    }
    static class link {
        public link(int max) {
            if (max > 0) {
                this.max = max;
            } else {
                throw new InvalidParameterException("参数不能小于0");
            }
        }
        // 最大能添加的元素的个数
        int max;
        // 链表中实际已经存储的元素的个数
        int size;
        // 链表的头节点
        Node first;
        // 链表的尾节点
        Node last;
        // 链表中的每个节点
        @AllArgsConstructor
        class Node {
            // 节点中保存的数据
            Object item;
            // 指向上一个节点
            Node pre;
            // 指向下一个节点
            Node next;
        }
        public void add(Object item) {
            Node node = new Node(item, null, null);
            if (size == 0) {
                size++;
                node.pre = null;
                node.next = null;
                first = node;
                last = node;
            } else {
                if (size == max) {
                    // 链表中的数据已经满了
                    // 策略1:先进先出,需要删除头节点
                    Node second = first.next;
                    second.pre = null;
                    first.next = null;
                    first = second;
                    size--;
                }
                // 将数据添加到链表尾部
                size++;
                last.next = node;
                node.pre = last;
                last = node;
            }
        }
        public void sout() {
            Node node = first;
            for (int i = 0; i < max; i++) {
                if (size == 0) {
                    System.out.println("链表中没有元素");
                    break;
                } else {
                    System.out.println(node.item);
                    node = node.next;
                    if (node == null) {
                        System.out.println("遍历结束");
                        break;
                    }
                }
            }
        }
    }
}

输出结果如下:

4
5
6
7

最少使用策略(LFU,Least Frequently Out)


根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。


下面给出两种实现方式


JDK自带优先级队列进行实现


实现思路:利用PriorityQueue小顶堆的特点,关于PriorityQueue可参考我之前的文章 java读源码 之 queue源码分析(PriorityQueue,附图)。每次出队的一定是优先级最低的元素,同时我们自定义比较器,我这里是通过用实体实现Comparable接口的方式。


创建类如下:

package com.study.spring.transaction.huancun;
import java.time.LocalDateTime;
/**
 * 进行一个简单的抽象,所有实体都要通过访问时间跟访问次数进行比较
 * @author dmz
 * @date Create in 23:45 2019/8/10
 */
public interface Count extends Comparable<Count> {
    void setLastVisitTime(LocalDateTime localDateTime);
    LocalDateTime getLastVisitTime();
    int getCount();
    void setCount(int count);
    default void addCount() {
        setCount(getCount() + 1);
    }
    // 根据访问次数比较,次数相同的情况下比较访问时间
    @Override
    default int compareTo(Count o2) {
        return this.getCount() == o2.getCount() ? this.getLastVisitTime().compareTo(o2.getLastVisitTime())
                : this.getCount() - o2.getCount();
    }
}
package com.study.spring.transaction.huancun;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * @author dmz
 * @date Create in 23:45 2019/8/10
 */
@Data
public class Entity implements Count, Comparable<Entity> {
    private int count;
    private Object object;
    private LocalDateTime lastVisitTime = LocalDateTime.now();
    Entity(Object object) {
        this.object = object;
    } 
}
package com.study.spring.transaction.huancun;
import java.time.LocalDateTime;
import java.util.PriorityQueue;
/**
 * @author dmz
 * @date Create in 23:55 2019/8/10
 */
public class LFUStrategy<T extends Count> {
    private int max;
    LFUStrategy(PriorityQueue<T> priorityQueue, int max) {
        this.max = max;
        this.priorityQueue = priorityQueue;
    }
    private PriorityQueue<T> priorityQueue;
    /**
     * 访问排列在index位置的元素
     *
     * @param index 位置
     * @return 所在元素
     */
    public T get(int index) {
        int count = 1;
        for (T t : priorityQueue) {
            if (count == index) {
                priorityQueue.remove(t);
                t.addCount();
                t.setLastVisitTime(LocalDateTime.now());
                // 先remove再add是为了利用其内部特性,调用add方法时会进行排序,将最小的元素置于堆顶
                priorityQueue.add(t);
                return t;
            }
        }
        return null;
    }
    @Override
    public String toString() {
        return "LFUStrategy{" +
                "priorityQueue=" + priorityQueue +
                '}';
    }
    public T remove() {
        return priorityQueue.poll();
    }
    public void add(T t) {
        if (max == priorityQueue.size()) {
            T remove = remove();
            System.out.println("执行LFU策略,删除的元素是:" + remove);
        } else {
            priorityQueue.offer(t);
        }
    }
}
package com.study.spring.transaction.huancun;
import java.util.PriorityQueue;
/**
 * @author dmz
 * @date Create in 0:08 2019/8/11
 */
public class Main {
    public static void main(String[] args) {
        PriorityQueue<Entity> priorityQueue = new PriorityQueue<>();
        for (int i = 1; i < 7; i++) {
            Entity entity = new Entity(i);
            priorityQueue.add(entity);
            try {
                // 为了将访问时间隔开
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 最大容忍6个元素,元素满了后,执行LFU策略
        LFUStrategy lfuStrategy = new LFUStrategy<>(priorityQueue, 6);
        // 对第一个元素进行访问
//        lfuStrategy.get(1);
        lfuStrategy.add(new Entity(7));
    }
}

在上述代码中,如果我们不访问任何元素,直接执行 lfuStrategy.add(new Entity(7)),那么就会直接移除第一个元素,但是如果我们访问第一个元素,那么就会移除第二个元素。大家可以自己试验,我就不贴运行结果了,太占篇幅


链表实现


实现思路:相比于上一种实现方式,,这里最大的不同就是要自己去实现排序的过程。我们还是直接用JDK中的LinkedList,进行实现。


package com.study.spring.transaction.huancun;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.LinkedList;
/**
 * @author dmz
 * @date Create in 12:03 2019/8/11
 */
@Data
@AllArgsConstructor
public class LFUStrategySecond<T extends Count> {
    private int max;
    private LinkedList<T> linkedList;
    public void add(T t) {
        if (linkedList.size() == max) {
            System.out.println("链表实现LFU策略删除元素:" + remove());
        }
        linkedList.add(t);
    }
    public T remove() {
        return linkedList.removeFirst();
    }
    public T get(int index) {
        T t = linkedList.get(index);
        t.addCount();
        t.setLastVisitTime(LocalDateTime.now());
        // 最小的放到链表头
        linkedList.sort((Comparator<T>) (o1, o2) ->
                o1.getCount() == o2.getCount() ? o1.getLastVisitTime().compareTo(o2.getLastVisitTime())
                        : o1.getCount() - o2.getCount()
        );
        return t;
    }
}
ackage com.study.spring.transaction.huancun;
import java.util.LinkedList;
/**
 * @author dmz
 * @date Create in 12:10 2019/8/11
 */
public class MainSecond {
    public static void main(String[] args) {
        LinkedList<Entity> linkedList = new LinkedList<>();
        linkedList.add(new Entity(1));
        linkedList.add(new Entity(2));
        linkedList.add(new Entity(3));
        linkedList.add(new Entity(4));
        linkedList.add(new Entity(5));
        LFUStrategySecond<Entity> lfuStrategySecond = new LFUStrategySecond<Entity>(5, linkedList);
        // 可以观察,进行访问跟不进行访问,删除元素的区别
  //      lfuStrategySecond.get(0);
        lfuStrategySecond.add(new Entity(6));
    }
}

最近最少使用策略(LRU,Least Recently Used)


根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。


实现思路:我们还是直接利用JDK的Linkedlist来进行实现,只要每次将被访问的元素置于链尾,若空间不够堆链头的数据进行删除即可。

package com.study.spring.transaction.huancun;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.LinkedList;
/**
 * @author dmz
 * @date Create in 12:25 2019/8/11
 */
@Data
@AllArgsConstructor
public class LRUStrategy<T> {
    // 不能小于1
    int max;
    LinkedList<T> linkedList = new LinkedList<>();
    public T get(int index) {
        // 每次获取元素时,将获取到的元素放到链尾
        T t = linkedList.get(index);
        linkedList.remove(t);
        linkedList.addLast(t);
        return t;
    }
    public T remove() {
        return linkedList.removeFirst();
    }
    public void add(T t) {
        if (linkedList.size() == max) {
            System.out.println("LRU删除的元素:"+remove());
        }
        linkedList.add(t);
    }
}

总结:


到这里三种缓存过期策略我们就介绍完了,可能对于最后一种LRU算法介绍的不是很详细,本来是想自定义一个单链表来实现的,不过这两天写链表都写吐了,实在不想写了,偷了个懒,大家可以自己实现下,有任何问题都可以给我留言哦,一定会回复的!
/

相关文章
|
2月前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
95 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
1月前
|
存储 缓存 NoSQL
缓存加速新玩法,让你的应用飞起来
本文主要叙述如何运用云数据库 Tair 构建缓存,助力应用提速、优化性能。
|
15天前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
77 29
|
15天前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
72 25
|
3月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
99 5
|
2月前
|
存储 缓存 算法
探索企业文件管理软件:Python中的哈希表算法应用
企业文件管理软件依赖哈希表实现高效的数据管理和安全保障。哈希表通过键值映射,提供平均O(1)时间复杂度的快速访问,适用于海量文件处理。在Python中,字典类型基于哈希表实现,可用于管理文件元数据、缓存机制、版本控制及快速搜索等功能,极大提升工作效率和数据安全性。
74 0
|
3月前
|
机器学习/深度学习 人工智能 算法
探索人工智能中的强化学习:原理、算法与应用
探索人工智能中的强化学习:原理、算法与应用
|
3月前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
77 1
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
99 1
|
3月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
69 5