Set keys=Map.keyset()怎么理解

Set keys=Map.keyset()怎么理解,第1张

有一个Map对象,这时候使用keySet()方法获取所有的key值,比如:

Map map = new HashMap();

mapput(1, "a");

mapput(2, "b");

mapput(3, "c");

mapput(4, "d");

Set keys1 = mapkeySet();

Set keys2 = mapkeySet();

Set keys3 = mapkeySet();

上面三个set对象key1,key2,key3引用的是一个对象。这时map的keySet()方法只返回一个set实例,所以当从key1中删除一个对象时候,key2和key3将会受到影响。

keys1remove(1);

Systemoutprintln(keys1);

Systemoutprintln(keys2);

Systemoutprintln(keys3);

打印结果为:

[2, 4, 3]

[2, 4, 3]

[2, 4, 3]

扩展资料

Map是java中的一个接口,MapEntry是Map的一个内部接口。对于Map中提供的一些常用方法,如mapkeySet()、mapentrySet()、mapvalues()等方法大多数人都不是很熟悉。

mapkeySet()方法返回值是Map中key值的集合;mapentrySet()的返回值是一个Set集合,此集合的类型为MapEntry。MapEntry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。它表示的是Map中的一个实体(key-value)。

接口中有我们常用的getKey(),getValue方法。

主方法:

public static void main(String[] args) {

Map<Integer,String> map = new HashMap<Integer,String>();

mapput(1, "雷达1");

mapput(2, "雷达2");

mapput(3, "雷达3");

mapput(4, "雷达4");

iterator(map);

}

hm已是一个HashMap的引用如果你知道当前的这个key,可以通过hmget(key)方法来获得value获得key的方法hmkeySet();因为你不知道key是哪个其实该方法就是获得一个key的集合具体可以结合以下例子看看,里面有个迭代器用于遍历的Sets=hmkeySet();//通过keySet方法可获得所有key的集合,放在一个容器Set里面Iteratorit=siterator();//获得一个迭代器引用it,通过siterator方法好比使“指针”指向//set里面的第一个元素的位置while(ithasNext())//set里面如果有下一个{Integerkey=itnext();//返回当前set中的这个元素(因为set中都是放的key,“指针”指向下一个Systemoutprintln(hmget(key));//利用hmget(key)方法获得该key对应的value}

关于SharedPreferences顺便再复习一下:

SharedPreferences 是Android 中的内置API,它允许我们存取键值对形式的基础类型数据,像:boolean,float ,int , long , string 。这些数据将会持久化的存在,即使你的应用程序结束之后(注意:如果应用从手机上写在掉后,该程序对应的SharedPreferences将会消失,保存在路径:/data/data/<package_name>/shared_prefs 目录下)

注意:User Preferences 用户配置

严格来说,SharedPreferences最好不要用来保存“用户配置”,比如,用户选择了什么铃声,是否自动更新等等。如果要为应用创建用户配置,可以使用PreferenceActivity,它可以用来创建“用户配置”。(PreferenceActivity用来创建程序中的设置界面)

java集合里面的函数

java集合里面的函数_java集合1——— 从集合接口框架说起

百里方欣

原创

关注

0点赞·155人阅读

(一) java集合分类

之前大概分为三种,Set,List,Map三种,JDK5之后,增加Queue主要由Collection和Map两个接口衍生出来,同时Collection接口继承Iterable接口,所以我们也可以说java里面的集合类主要是由Iterable和Map两个接口以及他们的子接口或者其实现类组成。我们可以认为Collection接口定义了单列集合的规范,每次只能存储一个元素,而Map接口定义了双列集合的规范,每次能存储一对元素。

Iterable接口:主要是实现遍历功能

Collection接口: 允许重复

Set接口:无序,元素不可重复,访问元素只能通过元素本身来访问。

List接口:有序且可重复,可以根据元素的索引来访问集合中的元素。

Queue接口:队列集合

Map接口:映射关系,简单理解为键值对,Key不可重复,与Collection接口关系不大,只是个别函数使用到。

整个接口框架关系如下(来自百度百科):

(1) Iterable接口

1 内部定义的方法

java集合最源头的接口,实现这个接口的作用主要是集合对象可以通过迭代器去遍历每一个元素。

源码如下:

// 返回一个内部元素为T类型的迭代器(JDK15只有这个接口)

Iterator iterator();

// 遍历内部元素,action意思为动作,指可以对每个元素进行 *** 作(JDK18添加)

default void forEach(Consumer super T> action) {}

// 创建并返回一个可分割迭代器(JDK18添加),分割的迭代器主要是提供可以并行遍历元素的迭代器,可以适应现在cpu多核的能力,加快速度。

default Spliterator spliterator() {

return SpliteratorsspliteratorUnknownSize(iterator(), 0);

}

从上面可以看出,foreach迭代以及可分割迭代,都加了default关键字,这个是Java 8 新的关键字,以前接口的所有接口,具体子类都必须实现,而对于deafult关键字标识的方法,其子类可以不用实现,这也是接口规范发生变化的一点。

下面我们分别展示三个接口的调用:

11 iterator方法

public static void iteratorHasNext(){

List list=new ArrayList();

listadd("Jam");

listadd("Jane");

listadd("Sam");

// 返回迭代器

Iterator iterator=listiterator();

// hashNext可以判断是否还有元素

while(iteratorhasNext()){

//next()作用是返回当前指针指向的元素,之后将指针移向下个元素

Systemoutprintln(iteratornext());

}

}

当然也可以使用for-each loop方式遍历

for (String item : list) {

Systemoutprintln(item);

}

但是实际上,这种写法在class文件中也是会转成迭代器形式,这只是一个语法糖。class文件如下:

public class IterableTest {

public IterableTest() { }

public static void main(String[] args) {

iteratorHasNext();

}

public static void iteratorHasNext() {

List list = new ArrayList();

listadd("Jam");

listadd("Jane");

listadd("Sam");

Iterator iterator = listiterator();

Iterator var2 = listiterator();

while(var2hasNext()) {

String item = (String)var2next();

Systemoutprintln(item);

}

}

}

需要注意的一点是,迭代遍历的时候,如果删除或者添加元素,都会抛出修改异常,这是由于快速失败fast-fail机制。

public static void iteratorHasNext(){

List list=new ArrayList();

listadd("Jam");

listadd("Jane");

listadd("Sam");

for (String item : list) {

if(itemequals("Jam")){

listremove(item);

}

Systemoutprintln(item);

}

}

从下面的错误我们可以看出,第一个元素是有被打印出来的,也就是remove *** 作是成功的,只是遍历到第二个元素的时候,迭代器检查,发现被改变了,所以抛出了异常。

Jam

Exception in thread "main" javautilConcurrentModificationException

at javautilArrayList$ItrcheckForComodification(ArrayListjava:909)

at javautilArrayList$Itrnext(ArrayListjava:859)

at IterableTestiteratorHasNext(IterableTestjava:15)

at IterableTestmain(IterableTestjava:7)

12 forEach方法

其实就是把对每一个元素的 *** 作当成了一个对象传递进来,对每一个元素进行处理。

default void forEach(Consumer super T> action) {

ObjectsrequireNonNull(action);

for (T t : this) {

actionaccept(t);

}

}

```java

当然像ArrayList自然也是有自己的实现的,那我们就可以使用这样的写法,简洁优雅。forEach方法在java8中参数是`javautilfunctionConsumer`,可以称为消费行为或者说动作类型。

```java

listforEach(x -> Systemoutprint(x));

同时,我们只要实现Consumer接口,就可以自定义动作,如果不自定义,默认迭代顺序是按照元素的顺序。

public class ConsumerTest {

public static void main(String[] args) {

List list=new ArrayList();

listadd("Jam");

listadd("Jane");

listadd("Sam");

MyConsumer myConsumer = new MyConsumer();

Iterator it = listiterator();

listforEach(myConsumer);

}

static class MyConsumer implements Consumer {

@Override

public void accept(Object t) {

Systemoutprintln("自定义打印:" + t);

}

}

}

输出的结果:

自定义打印:Jam

自定义打印:Jane

自定义打印:Sam

13 spliterator方法

这是一个为了并行遍历数据元素而设计的迭代方法,返回的是Spliterator,是专门并行遍历的迭代器。以发挥多核时代的处理器性能,java默认在集合框架中提供了一个默认的Spliterator实现,底层也就是StreamisParallel()实现的,我们可以看一下源码:

// stream使用的就是spliterator

default Stream stream() {

return StreamSupportstream(spliterator(), false);

}

default Spliterator spliterator() {

return Spliteratorsspliterator(this, 0);

}

public static Stream stream(Spliterator spliterator, boolean parallel) {

ObjectsrequireNonNull(spliterator);

return new ReferencePipelineHead<>(spliterator,

StreamOpFlagfromCharacteristics(spliterator),

parallel);

}

使用的方法如下:

public static void spliterator(){

List list = ArraysasList("1", "2", "3","4","5","6","7","8","9","10");

// 获取可迭代器

Spliterator spliterator = listspliterator();

// 一个一个遍历

Systemoutprintln("tryAdvance: ");

spliteratortryAdvance(item->Systemoutprint(item+" "));

spliteratortryAdvance(item->Systemoutprint(item+" "));

Systemoutprintln("\n-------------------------------------------");

// 依次遍历剩下的

Systemoutprintln("forEachRemaining: ");

spliteratorforEachRemaining(item->Systemoutprint(item+" "));

Systemoutprintln("\n------------------------------------------");

// spliterator1:0~10

Spliterator spliterator1 = listspliterator();

// spliterator1:6~10 spliterator2:0~5

Spliterator spliterator2 = spliterator1trySplit();

// spliterator1:8~10 spliterator3:6~7

Spliterator spliterator3 = spliterator1trySplit();

Systemoutprintln("spliterator1: ");

spliterator1forEachRemaining(item->Systemoutprint(item+" "));

Systemoutprintln("\n------------------------------------------");

Systemoutprintln("spliterator2: ");

spliterator2forEachRemaining(item->Systemoutprint(item+" "));

Systemoutprintln("\n------------------------------------------");

Systemoutprintln("spliterator3: ");

spliterator3forEachRemaining(item->Systemoutprint(item+" "));

}

tryAdvance() 一个一个元素进行遍历

forEachRemaining() 顺序地分块遍历

trySplit()进行分区形成另外的 Spliterator,使用在并行 *** 作中,分出来的是前面一半,就是不断把前面一部分分出来

结果如下:

tryAdvance:

1 2

-------------------------------------------

forEachRemaining:

3 4 5 6 7 8 9 10

------------------------------------------

spliterator1:

8 9 10

------------------------------------------

spliterator2:

1 2 3 4 5

------------------------------------------

spliterator3:

6 7

还有一些其他的用法在这里就不列举了,主要是trySplit()之后,可以用于多线程遍历。理想的时候,可以平均分成两半,有利于并行计算,但是不是一定平分的。

2 Collection接口 extend Iterable

Collection接口可以算是集合类的一个根接口之一,一般不能够直接使用,只是定义了一个规范,定义了添加,删除等管理数据的方法。继承Collection接口的有List,Set,Queue,不过Queue定义了自己的一些接口,相对来说和其他的差异比较大。

21 内部定义的方法

源码如下:

boolean add(Object o) //添加元素

boolean remove(Object o) //移除元素

boolean addAll(Collection c) //批量添加

boolean removeAll(Collection c) //批量移除

void retainAll(Collection c) // 移除在c中不存在的元素

void clear() //清空集合

int size() //集合大小

boolean isEmpty() //是否为空

boolean contains(Object o) //是否包含在集合中

boolean containsAll(Collection c) //是否包含所有的元素

Iterator iterator() // 获取迭代器

Object[] toArray() // 转成数组

default boolean removeIf(Predicate super E> filter) {} // 删除集合中复合条件的元素,删除成功返回true

boolean equals(Object o)

int hashCode()

default Spliterator spliterator() {} //获取可分割迭代器

default Stream stream() {} //获取流

default Stream parallelStream() {} //获取并行流

里面获取并行流的方法parallelStream(),其实就是通过默认的ForkJoinPool(主要用来使用分治法(Divide-and-Conquer Algorithm)来解决问题),提高多线程任务的速度。我们可以使用ArrayList来演示一下平行处理能力。例如下面的例子,输出的顺序就不一定是1,2,3,可能是乱序的,这是因为任务会被分成多个小任务,任务执行是没有特定的顺序的。

List list = ArraysasList(1, 2, 3, 4, 5, 6, 7, 8, 9);

listparallelStream()

forEach(out::println);

22 继承Collection的主要接口

graph LR;

Collection -->List-有顺序,可重复

List-有顺序,可重复 -->LinkedList-使用链表实现,线程不安全

List-有顺序,可重复 -->ArrayList-数组实现,线程不安全

List-有顺序,可重复 -->Vector-数组实现,线程安全

Vector-数组实现,线程安全 -->Stack-堆栈,先进后出

Collection-->Set-不可重复,内部排序

Set-不可重复,内部排序-->HashSet-hash表存储

HashSet-hash表存储-->LinkHashSet-链表维护插入顺序

Set-不可重复,内部排序-->TreeSet-二叉树实现,排序

Collection-->Queue-队列,先进先出

221 List extend Collection

继承于Collection接口,有顺序,取出的顺序与存入的顺序一致,有索引,可以根据索引获取数据,允许存储重复的元素,可以放入为null的元素。

最常见的三个实现类就是ArrayList,Vector,LinkedList,ArrayList和Vector都是内部封装了对数组的 *** 作,唯一不同的是,Vector是线程安全的,而ArrayList不是,理论上ArrayList *** 作的效率会比Vector好一些。

里面是接口定义的方法:

int size(); //获取大小

boolean isEmpty(); //判断是否为空

boolean contains(Object o); //是否包含某个元素

Iterator iterator(); //获取迭代器

Object[] toArray(); // 转化成为数组(对象)

T[] toArray(T[] a); // 转化为数组(特定位某个类)

boolean add(E e); //添加

boolean remove(Object o); //移除元素

boolean containsAll(Collection> c); // 是否包含所有的元素

boolean addAll(Collection extends E> c); //批量添加

boolean addAll(int index, Collection extends E> c); //批量添加,指定开始的索引

boolean removeAll(Collection> c); //批量移除

boolean retainAll(Collection> c); //将c中不包含的元素移除

default void replaceAll(UnaryOperator operator) {}//替换

default void sort(Comparator super E> c) {}// 排序

void clear();//清除所有的元素

boolean equals(Object o);//是否相等

int hashCode(); //计算获取hash值

E get(int index); //通过索引获取元素

E set(int index, E element);//修改元素

void add(int index, E element);//在指定位置插入元素

E remove(int index);//根据索引移除某个元素

int indexOf(Object o); //根据对象获取索引

int lastIndexOf(Object o); //获取对象元素的最后一个元素

ListIterator listIterator(); // 获取List迭代器

ListIterator listIterator(int index); // 根据索引获取当前的位置的迭代器

List subList(int fromIndex, int toIndex); //截取某一段数据

default Spliterator spliterator(){} //获取可切分迭代器

上面的方法都比较简单,值得一提的是里面出现了ListIterator,这是一个功能更加强大的迭代器,继承于Iterator,只能用于List类型的访问,拓展功能例如:通过调用listIterator()方法获得一个指向List开头的ListIterator,也可以调用listIterator(n)获取一个指定索引为n的元素的ListIterator,这是一个可以双向移动的迭代器。

*** 作数组索引的时候需要注意,由于List的实现类底层很多都是数组,所以索引越界会报错IndexOutOfBoundsException。

说起List的实现子类:

ArrayList:底层存储结构是数组结构,增加删除比较慢,查找比较快,是最常用的List集合。线程不安全。

LinkedList:底层是链表结构,增加删除比较快,但是查找比较慢。线程不安全。

Vector:和ArrayList差不多,但是是线程安全的,即同步。

222 Set extend Collection

Set接口,不允许放入重复的元素,也就是如果相同,则只存储其中一个。

下面是源码方法:

int size(); //获取大小

boolean isEmpty(); //是否为空

boolean contains(Object o); //是否包含某个元素

Iterator iterator(); //获取迭代器

Object[] toArray(); //转化成为数组

T[] toArray(T[] a); //转化为特定类的数组

boolean add(E e); //添加元素

boolean remove(Object o); //移除元素

boolean containsAll(Collection> c); //是否包含所有的元素

boolean addAll(Collection extends E> c); //批量添加

boolean retainAll(Collection> c); //移除所有不存在于c集合中的元素

boolean removeAll(Collection> c); //移除所有在c集合中存在的元素

void clear(); //清空集合

boolean equals(Object o); //是否相等

int hashCode(); //计算hashcode

default Spliterator spliterator() {} //获取可分割迭代器

主要的子类:

HashSet

允许空值

通过HashCode方法计算获取hash值,确定存储位置,无序。

LinkedHashSet

HashSet的子类

有顺序

TreeSet

如果无参数构建Set,则需要实现Comparable方法。

亦可以创建时传入比较方法,用于排序。

223 Queue extend Collection

队列接口,在Collection接口的接触上添加了增删改查接口定义,一般默认是先进先出,即FIFO,除了优先队列和栈,优先队列是自己定义了排序的优先顺序,队列中不允许放入null元素。

下面是源码:

boolean add(E e); //插入一个元素到队列,失败时返回IllegalStateException (如果队列容量不够)

boolean offer(E e); //插入一个元素到队列,失败时返回false

E remove(); //移除队列头的元素并移除

E poll(); //返回并移除队列的头部元素,队列为空时返回null

E element(); //返回队列头元素

E peek(); //返回队列头部的元素,队列为空时返回null

主要的子接口以及实现类有:

Deque(接口):Queue的子接口,双向队列,可以从两边存取

ArrayDeque:Deque的实现类,底层用数组实现,数据存贮在数组中

AbstractQueue:Queue的子接口,仅实现了add、remove和element三个方法

PriorityQueue:按照默认或者自己定义的顺序来排序元素,底层使用堆(完全二叉树)实现,使用动态数组实现,

BlockingQueue: 在javautilconcurrent包中,阻塞队列,满足当前无法处理的 *** 作。

(2) Map接口

定义双列集合的规范Map,每次存储一对元素,即key和value。

key的类型可以和value的类型相同,也可以不同,任意的引用类型都可以。

key是不允许重复的,但是value是可以重复的,所谓重复是指计算的hash值系统。

下面的源码的方法:

V put(K key, V value); // 添加元素

V remove(Object key); // 删除元素

void putAll(Map extends K, extends V> m); // 批量添加

void clear() // 移除所有元素

V get(Object key); // 通过key查询元素

int size(); // 查询集合大小

boolean isEmpty(); // 集合是否为空

boolean containsKey(Object key); // 是否包含某个key

boolean containsValue(Object value); // 是否包含某个value

Set keySet(); // 获取所有key的set集合

Collection values(); // 获取所有的value的set集合

Set> entrySet(); // 返回键值对的set,每一个键值对是一个entry对象

boolean equals(Object o); // 用于比较的函数

int hashCode(); // 计算hashcode

default V getOrDefault(Object key, V defaultValue) // 获取key对应的Value,没有则返回默认值()

default void forEach(BiConsumer super K, super V> action) {} // 遍历

default void replaceAll(BiFunction super K, super V, extends V> function) {} // 批量替换

// 缺少这个key的时候才会添加进去

// 返回值是是key对应的value值,如果不存在,则返回的是刚刚放进去的value

default V putIfAbsent(K key, V value) {}

default boolean remove(Object key, Object value) {} // 移除元素

default boolean replace(K key, V oldValue, V newValue) {} // 替换

default V replace(K key, V value) {} //替换

// 和putIfAbsent有点像,只不过传进去的mappingFunction是映射函数,也就是如果不存在key对应的value,将会执行函数,函数返回值会被当成value添加进去,同时返回新的value值

default V computeIfAbsent(K key,Function super K, extends V> mappingFunction) {}

// 和computeIfAbsent方法相反,只有key存在的时候,才会执行函数,并且返回

default V computeIfPresent(K key,BiFunction super K, super V, extends V> remappingFunction) {}

// 不管如何都会执行映射函数,返回value

default V compute(K key,BiFunction super K, super V, extends V> remappingFunction) {}

default V merge(K key, V value,BiFunction super V, super V, extends V> remappingFunction) {}

值得注意的是,Map里面定义了一个Entry类,其实就是定义了一个存储数据的类型,一个entry就是一个

Map的常用的实现子类:

HashMap:由数组和链表组成,线程不安全,无序。

LinkedHashMap:如果我们需要是有序的,那么就需要它,时间和空间效率没有HashMap那么高,底层是维护一条双向链表,保证了插入的顺序。

ConcurrentHashMap:线程安全,17JDK使用锁分离,每一段Segment都有自己的独立锁,相对来说效率也比较高。JDK18抛弃了Segment,使用Node数组+链表和红黑树实现,在线程安全控制上使用Synchronize和CAS,可以认为是优化的线程安全的HashMap。

HashTable:对比与HashMap主要是使用关键字synchronize,加上同步锁,线程安全。

(二)总结

这些集合原始接口到底是什么?为什么需要?

我想,这些接口其实都是一种规则/规范的定义,如果不这么做也可以,所有的子类自己实现,但是从迭代以及维护的角度来说,这就是一种抽象或者分类,比如定义了Iterator接口,某一些类就可以去继承或者实现,那就得遵守这个规范/契约。可以有所拓展,每个子类的拓展不一样,所以每个类就各有所长,但是都有一个中心,就是原始的集合接口。比如实现Map接口的所有类的中心思想都不变,只是各有所长,各分千秋,形成了大千集合世界。

作者简介:

秦怀,公众号秦怀杂货店作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析,JDBC,Mybatis,Spring,redis,分布式,剑指Offer,LeetCode等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。

平日时间宝贵,只能使用晚上以及周末时间学习写作,关注我,我们一起成长吧~

在完成事件接入的需求时,我们需要记录上一个批次拉取的事件,并与当前拉取到的事件做出比对,从而进行差分。我们目前的做法是使用redis来进行缓存:将上一个批次拉取到的事件缓存到一个list中。但是当事件数量过多时,value的大小会超过1M的限制,直接抛出异常。这其实是Tair出于性能的考虑而做出的限制,本文将谈谈我个人对于bigKey的理解。

顾名思义,bigKey指一个key对应的value占据的内存空间相对比较大,bigKey通常会有两种表现形式:

bigKey一旦产生,将会对tair的性能以及稳定性造成较大的影响,下面我将详细介绍一下bigKey的危害。

bigKey给tair带来的危害是多方面的,性能下降只是其中的一方面,极端情况下,bigKey甚至会导致缓存服务崩溃。下面我将从几个角度进行分析。

我们可以看到:

另外,在Redis执行异步重写 *** 作时(bgrewriteaof),主线程会fork出一个子进程来执行重写命令,这个子进程会与主线程共享内存。当主线程收到了新增或者修改一个key的命令,主线程会申请一块额外的内存空间来保存数据。但如果这个key是一个bigKey时,主线程会去申请一块更大空间,同样会阻塞主线程(与JVM分配内存一样,涉及锁和同步)。如果申请不到足够的空间,会导致Swap甚至会有OOM的风险,这同样会降低Redis的性能和稳定性。

Tair中一个key最大为1M,我们就以1M举例,当访问这个key的QPS为1000时,每秒将会有1GB左右的流量,对于带宽来说将是一个较大压力。如果这个bigKey是一个热点key时,后果将不堪设想。

如果主从同步的 client-output-buffer-limit 设置过小,并且 master 存在大量bigKey(数据量很大),主从全量同步时可能会导致 buffer 溢出,溢出后主从全量同步就会失败。如果主从集群配置了哨兵,那么哨兵会让 slave 继续向 master 发起全量同步请求,然后 buffer 又溢出同步失败,如此反复,会形成复制风暴,这会浪费 master 大量的 CPU、内存、带宽资源,也会让 master 产生阻塞的风险。 另外,当我们使用Redis Cluster时,由于Redis Cluster采用了同步迁移的方式,bigKey同样会阻塞主线程。这里提一下Codis,Codis在迁移bigKey时,使用了异步迁移 + 指令拆分的方式,对于bigKey (集合类型) 中每个元素,用一条指令进行迁移,而不是把整个 bigKey 进行序列化后再整体传输。这种化整为零的方式,就避免了 bigKey 迁移时,因为要序列化大量数据而阻塞的问题。

当我们写入或者读取大量bigKey的时候,很有可能导致输入/输出缓冲区溢出。如果客户端占用的内存总量超过了服务器设置的maxmemory时(默认4GB),将会直接触发服务器的内存淘汰策略,如果有数据被淘汰,再要获取这些数据就需要到后端回源,间接降低了缓存系统的性能。同时,淘汰的如果是bigKey也同样会阻塞主线程。另外,在极端情况下,多个客户端占用了过多的内存将导致OOM,进而使得整个redis进程崩溃。

使用切片集群的时候,我们通常会将不同的key存放在不同的实例上,如果存在bigKey的话,会导致相应实例的数据量增大,内存压力也相应增大。

常用的做法是通过/redis-cli --bigkeys命令对整个redis中的键值对进行统计,输出每种数据类型中最大的 bigkey 的信息。一般会配合-i参数一起使用,控制扫描间隔,避免长时间扫描降低 Redis 实例的性能。另外该命令不要在业务高峰期使用。

或者我们可以通过debug object key 命令去查看serializedlength属性,serializedlength表示key对应的value序列化后的字节数,通过观察serializedlength的大小可以辅助排查bigKey。使用scan + debug object key命令,我们可以计算其中每个key的serializedlength,进而发现其中的bigKey,并做好相应的监控和处理。不过对于集合类型的bigKey,debug object key 命令的执行效率不高,存在阻塞redis的风险。

另外,在读取bigKey的时候,我们尽量不要一次性将全部数据读取出来,而是采用分批的方式进行读取:利用scan命令进行渐进式遍历,将大量数据分批多次读取出来,减小redis的压力,避免阻塞的风险。

同样的,在删除bigKey的时候我们也可以使用scan命令来进行批量删除。如果你是用的redis是40之后的版本,则可以利用unlink命令配合lazy free配置(需要手动开启)来进行异步删除,避免主线程阻塞。

OrderedDict类包含在collections模块中。

创建有序字典

clear: 清空有序字典

copy: 复制有序字典

fromkeys: 列表转换

items: 返回“键值对”列表

keys: 获取所有key

move_to_end: 指定一个key,把对应的key-value移到最后

pop: 获取指定key的value,并在字典中删除

popitem: 按照后进先出原则,删除最后加入的元素,返回key-value

setdefault: 获取指定key的value,如果key不存在,则创建

values: 获取字典所有的value,返回一个列表

普通字典不考虑存储顺序,有序字典的顺序与加入键值的顺序有关。

但是调用keys方法时,返回值不考虑顺序。

以上就是关于Set keys=Map.keyset()怎么理解全部的内容,包括:Set keys=Map.keyset()怎么理解、根据map的key值获取value值 有多个key怎么获取多个value值、如何获取sharedpreferences的所有key等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9323357.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存