1、布隆过滤器
public class BloomFilter {
/**
* 1、首先需要初始化一个二进制的数组,长度设为 L,
* 同时初始值全为 0 。
*
* 2、当写入一个 A1=1000 的数据时,需要进行 H 次 hash 函数的运算(这里为 2 次);
* 与 HashMap 有点类似,通过算出的 HashCode 与 L 取模后定位到 0、2 处,将该处的值设为 1。
* A2=2000 也是同理计算后将 4、7 位置设为 1。
*
* 3、当有一个 B1=1000 需要判断是否存在时,也是做两次 Hash 运算,
* 定位到 0、2 处,此时他们的值都为 1 ,所以认为 B1=1000 存在于集合中。
* 当有一个 B2=3000 时,也是同理。第一次 Hash 定位到 index=4 时,数组中的值为 1,
* 所以再进行第二次 Hash 运算,结果定位到 index=5 的值为 0,所以认为 B2=3000 不存在于集合中。
*/
/**
* 数组长度
*/
private int arraySize;
/**
* 数组
*/
private int[] array;
public BloomFilter(int arraySize) {
this.arraySize = arraySize;
array = new int[arraySize];
}
public static void main(String[] args) {
BloomFilter bloomFilter = new BloomFilter(10);
bloomFilter.add("link");
System.out.println(bloomFilter.check("link"));
}
/**
* 写入数据
* @param key
*/
public void add(String key) {
int first = hashcode_1(key);
int second = hashcode_2(key);
int third = hashcode_3(key);
array[first % arraySize] = 1;
array[second % arraySize] = 1;
array[third % arraySize] = 1;
}
/**
* 判断数据是否存在
* @param key
* @return
*/
public boolean check(String key) {
int first = hashcode_1(key);
int second = hashcode_2(key);
int third = hashcode_3(key);
int firstIndex = array[first % arraySize];
if (firstIndex == 0) {
return false;
}
int secondIndex = array[second % arraySize];
if (secondIndex == 0) {
return false;
}
int thirdIndex = array[third % arraySize];
if (thirdIndex == 0) {
return false;
}
return true;
}
/**
* hash 算法1
* @param key
* @return
*/
private int hashcode_1(String key) {
int hash = 0;
int i;
for (i = 0; i < key.length(); ++i) {
hash = 33 * hash + key.charAt(i);
}
return Math.abs(hash);
}
/**
* hash 算法2
* @param data
* @return
*/
private int hashcode_2(String data) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < data.length(); i++) {
hash = (hash ^ data.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return Math.abs(hash);
}
/**
* hash 算法3
* @param key
* @return
*/
private int hashcode_3(String key) {
int hash, i;
for (hash = 0, i = 0; i < key.length(); ++i) {
hash += key.charAt(i);
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return Math.abs(hash);
}
}
2、一致性hash
一致性hash算法
1、初始化一个长度为 N 的数组。
2、将服务节点通过 hash 算法得到的正整数,同时将节点自身的数据(hashcode、ip、端口等)存放在这里。
3、完成节点存放后将整个数组进行排序(排序算法有多种)。
4、客户端获取路由节点时,将自身进行 hash 也得到一个正整数;
5、遍历这个数组直到找到一个数据大于等于当前客户端的 hash 值,就将当前节点作为该客户端所路由的节点。
6、如果没有发现比客户端大的数据就返回第一个节点(满足环的特性)。
- TreeMap实现
public class ConsistentHashing {
public static void main(String[] args) {
// 基于红黑树
TreeMap<Long, String> map = new TreeMap<>();
map.put(1L, "127.0.0.1");
map.put(11L, "127.0.0.11");
map.put(6L, "127.0.0.6");
map.put(21L, "127.0.0.21");
// tailMap 可以获取比当前 key 大的部分数据
SortedMap<Long, String> last = map.tailMap(7L);
if (!last.isEmpty()) {
System.out.println(last.get(last.firstKey()));
} else {
System.out.println(map.firstEntry().getValue());
}
}
}
- 自定义实现
扩展性是对路由算法来说的,比如它需要支持轮询、
hash、一致性hash、随机、LRU等
// AbstractConsistentHash 一致性抽象
public abstract class AbstractConsistentHash {
/**
* 添加节点
*
* @param key
* @param value
*/
public abstract void add(long key, String value);
/**
* 节点排序,默认自带排序时不用排序
*/
public void sort() {
}
/**
* 根据key获取第一个值
*
* @param value
* @return
*/
public abstract String getFirstNodeValue(String value);
/**
* 传入节点列表,返回一个节点
*
* @param values
* @param key
* @return
*/
public String process(List<String> values, String key) {
for (String value : values) {
add(hash(key), value);
}
sort();
return getFirstNodeValue(key);
}
/**
* hash函数
* @param key
* @return
*/
public Long hash(String key) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("md5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("md5 not supported", e);
}
md5.reset();
byte[] keyBytes = null;
keyBytes = key.getBytes(StandardCharsets.UTF_8);
md5.update(keyBytes);
byte[] digest = md5.digest();
long hashCode = ((long) (digest[3] & 0xFF) << 24)
| ((long) (digest[2] & 0xFF) << 16)
| ((long) (digest[3] & 0xFF) << 8)
| (digest[0] & 0xFF);
long truncateHashCode = hashCode & 0xffffffffL;
return truncateHashCode;
}
}
public class SortArrayMap {
private Node[] buckets;
private static final int DEFAULT_SIZE = 10;
private int size = 0;
public SortArrayMap() {
this.buckets = new Node[DEFAULT_SIZE];
}
private class Node {
public Long key;
public String value;
public Node(Long key, String value) {
this.key = key;
this.value = value;
}
}
public void add(Long key, String value) {
checkSize(size + 1);
Node node = new Node(key, value);
buckets[size++] = node;
}
public void sort() {
Arrays.sort(buckets, 0, size, new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
return o1.key > o2.key ? 1 : -1;
}
});
}
public String firstNodeValue(Long key) {
if (size == 0) {
return null;
}
for (Node bucket : buckets) {
if (bucket == null){
continue;
}
if (bucket.key >= key){
return bucket.value;
}
}
return null;
}
private void checkSize(int size) {
if (size >= buckets.length) {
int oldLen = buckets.length;
int newLen = oldLen + (oldLen >> 1);
buckets = Arrays.copyOf(buckets, newLen);
}
}
}
public class SortArrayMapConsistentHash extends AbstractConsistentHash {
private SortArrayMap sortArrayMap = new SortArrayMap();
/**
* 每个实际节点映射的虚拟节点数
*/
private static final int VIRTUAL_NODE_SIZE = 2;
@Override
public void add(long key, String value) {
for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) {
Long hash = super.hash("vir" + key + i);
sortArrayMap.add(hash, value);
}
sortArrayMap.add(key, value);
}
@Override
public void sort() {
sortArrayMap.sort();
}
@Override
public String getFirstNodeValue(String value) {
long hash = super.hash(value);
return sortArrayMap.firstNodeValue(hash);
}
public static void main(String[] args) {
AbstractConsistentHash map = new SortArrayMapConsistentHash();
List<String> ips = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ips.add("127.0.0." + i);
}
String process = map.process(ips, "link");
System.out.println(process);
}
}
- 实现2
public class TreeMapConsistentHash extends AbstractConsistentHash{
private TreeMap<Long, String> treeMap = new TreeMap<>();
/**
* 虚拟节点数
*/
private static final int VIRTUAL_NODE_SIZE = 2;
@Override
public void add(long key, String value) {
for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) {
Long hash = super.hash("vir" + key + i);
treeMap.put(hash, value);
}
treeMap.put(key, value);
}
@Override
public String getFirstNodeValue(String value) {
long hash = super.hash(value);
SortedMap<Long, String> last = treeMap.tailMap(hash);
if (!last.isEmpty()){
return last.get(last.firstKey());
}
return treeMap.firstEntry().getValue();
}
public static void main(String[] args) {
AbstractConsistentHash map = new TreeMapConsistentHash();
List<String> ips = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ips.add("127.0.0." + i);
}
String process = map.process(ips, "link");
System.out.println(process);
}
}
3、微信抢红包算法
一般情况:
1.所有人抢到金额之和等于红包金额
2.每个人抢到不能少于最小值
3.要保证所有人抢到金额的几率相等
4.完全分配所有金额
特殊情况:
带有上下边界的分配
例如:总金额:100,个数:10,最小面值:1,最大面值:20
假如前面九个均抢到1,那剩下最后一个为91
没有最大的范围限制
方法1:二倍均值法
* M: 剩余红包金额
* N: 剩余人数
* 每次抢到的金额 = 随机区间 (0, M / N X 2)
* 例:
* 假设有10个人,红包总额100元。
* 100/10X2 = 20, 所以第一个人的随机范围是(0,20 ),平均可以抢到10元。
*
* 假设第一个人随机到10元,那么剩余金额是100-10 = 90 元。
* 90/9X2 = 20, 所以第二个人的随机范围同样是(0,20 ),平均可以抢到10元。
*
* 缺点:
* 每次抢到金额小于的平均的2倍
方法2:线段切割法
* 当N个人一起抢总金额为M的红包时,我们需要做N-1次随机运算,
* 以此确定N-1个切割点。随机的范围区间是(1, M)
*
* 注意:
* 1.当随机切割点出现重复,如何处理。
* 2.如何尽可能降低时间复杂度和空间复杂度。
方法3: 带上下限非对称平均值偏移修正随机
public class RedPacketTools {
private static final BigDecimal TIMES_100 = new BigDecimal(100);
private static BigDecimal MIN_MONEY = new BigDecimal("0.01");
private static BigDecimal MAX_MONEY = new BigDecimal(50);
private static final Random random = new Random();
public static List<BigDecimal> createBonusList(BigDecimal totalMoney, int totalCount) {
List<BigDecimal> moneys = new ArrayList<>(totalCount);
int rdMoney = totalMoney.multiply(TIMES_100).intValue();
int rdMin = MIN_MONEY.multiply(TIMES_100).intValue();
int rdMax = MAX_MONEY.multiply(TIMES_100).intValue();
List<Integer> _moneys = createBonusList(rdMoney, totalCount, rdMin, rdMax, 0.9);
for (int m : _moneys) {
moneys.add(new BigDecimal(m).divide(TIMES_100));
}
return moneys;
}
private static List<Integer> createBonusList(int totalBonus, int totalNum, int rdMin, int rdMax, double bigRate) {
int sendedBonus = 0;
int sendedNum = 0;
List<Integer> bonusList = new ArrayList<>();
while (sendedNum < totalNum) {
int bonus = randomBonusWithSpecifyBound(totalBonus, totalNum, sendedBonus, sendedNum, rdMin, rdMax, bigRate);
bonusList.add(bonus);
sendedNum++;
sendedBonus += bonus;
}
return bonusList;
}
/**
* 带上下线的随机生成:
* 总金币数量为S1,已发币量为S2
* 总份数为P1,已发份数为P2
* 平均值为S1/P1,则随机下限SMIN=(S1/P1)*0.1,随机上限SMAX=(S1/P1)*1.9
* 设生成的第n个随机币值为X,剩余获取总值为Y,则剩余币S1-S2=X+Y
* 同时X应处于限定随机上下限(SMIN~SMAX)之间,X的随机范围为:
* <p>
* MAX(S1–S2-(P1–P2-1)*SMAX,SMIN)<=X<=MIN(S1–S2–(P1–P2-1)*SMIN,SMAX)
* <p>
* bigRate:波动范围大小
*
* @return
*/
private static int randomBonusWithSpecifyBound(int totalBonus, int totalNum, int sendedBonus,
int sendedNum, int rdMin, int rdMax, double bigRate) {
double avg = totalBonus / (double) totalNum;
int leftLen = (int) Math.ceil(avg - rdMin);
int rightLen = (int) Math.floor(rdMax - avg);
int boundMin = 0;
int boundMax = 0;
// 大范围设置小概率
if (leftLen == rightLen) {
boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), rdMin);
boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), rdMax);
} else if (rightLen - leftLen > 0) {
// 上限偏离
int standardRdMax = (int) Math.ceil(avg + leftLen);
int _rdMax = canReward(bigRate) ? rdMax : standardRdMax;
boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMax), rdMin);
boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), _rdMax);
} else {
// 下限偏离
int standardRdMin = (int) Math.floor(avg - rightLen);
int _rdMin = canReward(bigRate) ? rdMin : standardRdMin;
boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), _rdMin);
boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMin), rdMax);
}
// 已发平均值偏移修正-动态比例
if (boundMin == boundMax) {
return getRandomVal(boundMin, boundMax);
}
double currAvg = sendedNum == 0 ? (double) avg : (sendedBonus / (double) sendedNum);
double middle = (boundMin + boundMax) / 2.0;
int subMin = boundMin, subMax = boundMax;
// 期望值
double exp = avg - (currAvg - avg) * sendedNum / (double) (totalNum - sendedNum);
if (middle > exp) {
subMax = (int) Math.round((boundMin + exp) / 2.0);
} else {
subMin = (int) Math.round((exp + boundMax) / 2.0);
}
int expBound = (boundMin + boundMax) / 2;
int expSub = (subMin + subMax) / 2;
double subRate = (exp - expBound) / (double) (expSub - expBound);
return getRandomValWithSpecifySubRate(boundMin, boundMax, subMin, subMax, subRate);
}
/**
* 返回一次抽奖在指定中奖概率下是否中奖
*/
private static boolean canReward(double rate) {
return Math.random() <= rate;
}
/**
* 返回min~max区间内随机数,含min和max
*/
private static int getRandomVal(int min, int max) {
return random.nextInt(max - min + 1) + min;
}
/**
* 带概率偏向的随机算法,概率偏向subMin~subMax区间
* 返回boundMin~boundMax区间内随机数(含boundMin和boundMax),同时可以指定子区间subMin~subMax的优先概率
* 例:传入参数(10, 50, 20, 30, 0.8),则随机结果有80%概率从20~30中随机返回,有20%概率从10~50中随机返回
*/
private static int getRandomValWithSpecifySubRate(int boundMin, int boundMax, int subMin, int subMax, double subRate) {
if (canReward(subRate)) {
return getRandomVal(subMin, subMax);
}
return getRandomVal(boundMin, boundMax);
}
/**
* 双边界法
*/
public static void doubleBoundMethod() {
BigDecimal totalMoney = new BigDecimal("100");
int totalAmount = 10;
List<BigDecimal> bonusList = createBonusList(totalMoney, totalAmount);
System.out.println(Arrays.toString(bonusList.toArray()));
BigDecimal sum = new BigDecimal(0);
for (BigDecimal m : bonusList) {
sum = sum.add(m);
}
System.out.println("sum:" + sum + ",人数:" + totalAmount);
}
/**
* 2倍均值法
* 特点:带下线的分割法
* 缺点:分配不均匀,
*/
public static List<Integer> redPacketDoubleMethod(int total, int count) {
List<Integer> list = new ArrayList<>();
int minLimit = 80 / 9;
int maxLimit = 20;
if (total < minLimit * count) {
throw new RuntimeException("金额过小、最小金额过大!");
}
while (count > 0) {
if (count == 0) {
list.add(total);
break;
}
int max = total / count * 2;
int money = (int) (Math.random() * max);
money = money < minLimit ? minLimit : money;
money = money > maxLimit ? maxLimit : money;
// 最大值限制
total -= money;
count--;
list.add(money);
}
return list;
}
public static void main(String[] args) {
redPacketDoubleMethod(100, 10);
doubleBoundMethod();
}
}
4、LRU缓存设计
- 方法1
实现一个 LRU 缓存:
* 1、当缓存数据达到 N 之后需要淘汰掉最近最少使用的数据。
* 2、N 小时之内没有被访问的数据也需要淘汰掉。
public class LRUAbstractMap extends java.util.AbstractMap {
/**
* 检查是否超期线程
*/
private ExecutorService checkTimePool;
/**
* map 最大size
*/
private final static int MAX_SIZE = 1024;
/**
* 缓存节点数据
*/
private final static ArrayBlockingQueue<Node> QUEUE = new ArrayBlockingQueue<>(MAX_SIZE);
/**
* 默认大小
*/
private final static int DEFAULT_ARRAY_SIZE = 1024;
/**
* 数组长度
*/
private int arraySize;
/**
* 数组
*/
private Object[] arrays;
/**
* 判断是否停止 flag
*/
private volatile boolean flag = true;
/**
* 超时时间
*/
private final static Long EXPIRE_TIME = 60 * 60 * 1000L;
/**
* 整个 Map 的大小
*/
private volatile AtomicInteger size;
public LRUAbstractMap() {
arraySize = DEFAULT_ARRAY_SIZE;
arrays = new Object[arraySize];
//开启一个线程检查最先放入队列的值是否超期
executeCheckTime();
}
@Override
public Set<Entry> entrySet() {
return super.keySet();
}
@Override
public int size() {
return size.get();
}
public int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
@Override
public Object put(Object key, Object value) {
int hash = hash(key);
int index = hash % arraySize ;
Node currentNode = (Node) arrays[index] ;
if (currentNode == null){
arrays[index] = new Node(null,null, key, value);
//写入队列
QUEUE.offer((Node) arrays[index]) ;
sizeUp();
}else {
Node cNode = currentNode ;
Node nNode = cNode ;
//存在就覆盖
if (nNode.key == key){
cNode.val = value ;
}
while (nNode.next != null){
//key 存在 就覆盖 简单判断
if (nNode.key == key){
nNode.val = value ;
break ;
}else {
//不存在就新增链表
sizeUp();
Node node = new Node(nNode,null,key,value) ;
//写入队列
QUEUE.offer(currentNode) ;
cNode.next = node ;
}
nNode = nNode.next ;
}
}
return null ;
}
@Override
public Object get(Object key) {
int hash = hash(key);
int index = hash % arraySize;
Node currentNode = (Node) arrays[index];
if (currentNode == null) {
return null;
}
if (currentNode.next == null) {
//更新时间
currentNode.setUpdateTime(System.currentTimeMillis());
//没有冲突
return currentNode;
}
Node nNode = currentNode;
while (nNode.next != null) {
if (nNode.key == key) {
//更新时间
currentNode.setUpdateTime(System.currentTimeMillis());
return nNode;
}
nNode = nNode.next;
}
return super.get(key);
}
/**
* 增加size
*/
private void sizeUp() {
//在put值时候认为里边已经有数据了
flag = true;
if (size == null) {
size = new AtomicInteger();
}
int size = this.size.incrementAndGet();
if (size >= MAX_SIZE) {
//找到队列头的数据
Node node = QUEUE.poll();
if (node == null) {
throw new RuntimeException("data error");
}
//移除该 key
Object key = node.key;
remove(key);
lruCallback();
}
}
@Override
public Object remove(Object key) {
int hash = hash(key);
int index = hash % arraySize;
Node currentNode = (Node) arrays[index];
if (currentNode == null) {
return null;
}
if (currentNode.key == key) {
sizeDown();
arrays[index] = null;
//移除队列
QUEUE.poll();
return currentNode;
}
Node nNode = currentNode;
while (nNode.next != null) {
if (nNode.key == key) {
sizeDown();
//在链表中找到了 把上一个节点的 next 指向当前节点的下一个节点
nNode.pre.next = nNode.next;
nNode = null;
//移除队列
QUEUE.poll();
return nNode;
}
nNode = nNode.next;
}
return super.remove(key);
}
private void lruCallback() {
System.out.println("lruCallback");
}
/**
* 数量减小
*/
private void sizeDown() {
if (QUEUE.size() == 0) {
flag = false;
}
this.size.decrementAndGet();
}
// *******************************************************//
/**
* 开启一个线程检查最先放入队列的值是否超期
* 设置为守护线程
*/
private void executeCheckTime() {
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("check-thread-%d")
.setDaemon(true)
.build();
checkTimePool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
checkTimePool.execute(new CheckTimeThread());
}
private class CheckTimeThread implements Runnable {
@Override
public void run() {
while (flag) {
try {
Node node = QUEUE.poll();
if (node == null) {
continue;
}
Long updateTime = node.getUpdateTime();
if ((updateTime - System.currentTimeMillis()) >= EXPIRE_TIME) {
remove(node.key);
}
} catch (Exception e) {
System.out.println("InterruptedException");
}
}
}
}
/**
* 链表节点
*/
private class Node {
private Node next;
private Node pre;
private Object key;
private Object val;
private Long updateTime;
public Node(Node pre, Node next, Object key, Object val) {
this.pre = pre;
this.next = next;
this.key = key;
this.val = val;
this.updateTime = System.currentTimeMillis();
}
public void setUpdateTime(Long updateTime) {
this.updateTime = updateTime;
}
public Long getUpdateTime() {
return updateTime;
}
@Override
public String toString() {
return "Node{" +
"key=" + key +
", val=" + val +
'}';
}
}
}
- 方法2
要求:
* 要记录最近最少使用,那至少需要一个有序的集合来保证写入的顺序。
* 在使用了数据之后能够更新它的顺序。
思路:
* 1、每次写入数据时将数据放入链表头结点。
* 2、使用数据时候将数据移动到头结点。
* 3、缓存数量超过阈值时移除链表尾部数据。
public class LRUMap<K,V> {
private final Map<K, V> cacheMap = new HashMap<>();
/**
* 最大缓存大小
*/
private int cacheSize;
/**
* 节点大小
*/
private int nodeCount;
/**
* 头结点
*/
private Node<K, V> header;
/**
* 尾结点
*/
private Node<K, V> tailer;
public LRUMap(int cacheSize) {
this.cacheSize = cacheSize;
header = new Node<>();
header.pre = null;
tailer = new Node<>();
tailer.next = null;
// 双向链表
header.next = tailer;
tailer.pre = header;
}
public void put(K key, V value) {
cacheMap.put(key, value);
addNode(key, value);
}
private void addNode(K key, V value) {
Node<K, V> node = new Node<>(key, value);
if (cacheSize == nodeCount) {
delTail();
}
addHead(node);
}
private void delTail() {
cacheMap.remove(tailer.getKey());
tailer.pre.next = null;
tailer = tailer.pre;
nodeCount--;
}
private void addHead(Node<K, V> node) {
//写入头结点
header.pre = node;
node.next = header;
header = node;
nodeCount++;
//如果写入的数据大于2个 就将初始化的头尾结点删除
if (nodeCount == 2) {
tailer.pre.pre.next = null;
tailer = tailer.pre.pre;
}
}
public V get(K key){
Node<K, V> node = getNode(key);
moveToHead(node) ;
return cacheMap.get(key);
}
private void moveToHead(Node<K,V> node){
//如果是最后的一个节点
if (node.next == null){
node.pre.next = null ;
tailer = node.pre ;
nodeCount -- ;
}
//如果是本来就是头节点 不作处理
if (node.pre == null){
return ;
}
//如果处于中间节点
if (node.next != null && node.pre != null){
//它的上一节点指向它的下一节点 也就删除当前节点
node.next.pre = node.pre ;
nodeCount -- ;
}
//最后在头部增加当前节点
//注意这里需要重新 new 一个对象,不然原本的node 还有着下面的引用,会造成内存溢出。
node = new Node<>(node.getKey(),node.getValue()) ;
addHead(node) ;
}
/**
* 链表查询 效率较低
* @param key
* @return
*/
private Node<K,V> getNode(K key){
Node<K,V> node = tailer ;
while (node != null){
if (node.getKey().equals(key)){
return node ;
}
node = node.pre ;
}
return null ;
}
private class Node<K, V> {
private K key;
private V value;
Node<K, V> next;
Node<K, V> pre;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
public Node() {
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder() ;
Node<K,V> node = tailer ;
while (node != null){
sb.append(node.getKey()).append(":")
.append(node.getValue())
.append("-->") ;
node = node.pre ;
}
return sb.toString();
}
}
- 设计3:LinkedHashMap实现
LRU 缓存机制 设计和实现一个 LRU(最近最少使用)缓存数据结构,使它应该支持一下操作:get 和 put。 get(key) - 如果 key 存在于缓存中,则获取 key 的 value(总是正数),否则返回 -1。 put(key,value) - 如果 key 不存在,请设置或插入 value。当缓存达到其容量时,它应该在插入新项目之前使最近最少使用的项目作废。
public class LRULinkedMap<K,V> {
/**
* 最大缓存大小
*/
private int cacheSize;
private LinkedHashMap<K,V> cacheMap ;
public LRULinkedMap(int cacheSize) {
this.cacheSize = cacheSize;
cacheMap = new LinkedHashMap(16,0.75F,true){
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
if (cacheSize + 1 == cacheMap.size()){
return true ;
}else {
return false ;
}
}
};
}
public void put(K key,V value){
cacheMap.put(key,value) ;
}
public V get(K key){
return cacheMap.get(key) ;
}
public Collection<Map.Entry<K, V>> getAll() {
return new ArrayList<Map.Entry<K, V>>(cacheMap.entrySet());
}
}
3134

被折叠的 条评论
为什么被折叠?



