侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

Java并发编程之ConcurrentNavigableMap原理及实战

孔子说JAVA
2022-06-30 / 0 评论 / 0 点赞 / 55 阅读 / 12,935 字 / 正在检测是否收录...

java.util.concurrent.ConcurrentNavigableMap接口是为了支持NavigableMap并发访问的而设计的子接口,使其子类可以实现并发访问。该接口继承自ConcurrentMap及NavigableMap接口,支持NavigableMap操作,并且递归地支持其可导航的子映射和近似匹配。既保证了线程安全性,又提供导航搜索子Map视图的功能。可以通过不同的方法,如 headMap(), subMap() 和tailMap(),返回这些子类。

image-1656549526204

  • java.util.concurrent.ConcurrentMap接口是Map接口的子接口,支持底层Map变量上的原子操作。 它具有获取和设置方法,如在变量上的读取和写入。 也就是说,一个集合与同一变量上的任何后续读取相关联。 该接口确保线程安全性和原子性保证。

  • NavigableMap 扩展了 SortedMap 接口,具有了针对给定搜索目标返回最接近匹配项的导航方法。

  • SortedMap(java.util.SortedMap)接口是Map的子接口,SortedMap中增加了元素的排序功能。可以使用Comparator的实现作为TreeMap 构造函数的参数 ,这个Comparator将用于对存储在SortedMap中的键、值对的键进行排序。

1、相关接口介绍

1.1 SortedMap方法

//可以自定义排序比较器
Comparator<? super K> comparator(); 

//按key升序排列,返回子映射,fromKey到toKey,包括fromKey,不包括toKey
SortedMap<K,V> subMap(K fromKey, K toKey);

//按key升序排列,返回子映射,开头到toKey,不包括toKey
SortedMap<K,V> headMap(K toKey);

//按key升序排列,返回子映射,fromKey到末尾,包括fromKey
SortedMap<K,V> tailMap(K fromKey);

//按key升序排列,返回第一个key
K firstKey();

//按key升序排列,返回最后一个key
K lastKey();

//返回key的集合,升序排列
Set<K> keySet();

//返回value的集合,按key升序排列,
Collection<V> values();

//返回Entry的集合,按key升序排列
Set<Map.Entry<K, V>> entrySet();

1.2 NavigableMap方法

NavigableMap继承了SortedMap:

public interface NavigableMap<K,V> extends SortedMap<K,V>

定义了一些导航方法:

// 找到第一个比指定的key小的值
Map.Entry<K,V> lowerEntry(K key);

// 找到第一个比指定的key小的key
K lowerKey(K key);

// 找到第一个小于或等于指定key的值
Map.Entry<K,V> floorEntry(K key);

// 找到第一个小于或等于指定key的key
K floorKey(K key);

//  找到第一个大于或等于指定key的值
Map.Entry<K,V> ceilingEntry(K key);

//找到第一个大于或等于指定key的key
K ceilingKey(K key);

// 找到第一个大于指定key的值
Map.Entry<K,V> higherEntry(K key);

// 找到第一个大于指定key的key
K higherKey(K key);

// 获取最小值
Map.Entry<K,V> firstEntry();

// 获取最大值
Map.Entry<K,V> lastEntry();

// 删除最小的元素
Map.Entry<K,V> pollFirstEntry();

// 删除最大的元素
Map.Entry<K,V> pollLastEntry();

//返回key降序排列的NavigableMap(视图),注意是视图,所以对它进行一个remove操作,也会影响到原来的Map的,是同一个引用
NavigableMap<K,V> descendingMap();

// 返回一个Navigable的key的集合,NavigableSet和NavigableMap类似(返回key升序排列的NavigableSet)
NavigableSet<K> navigableKeySet();

// 对上述集合倒序(返回key降序排列的NavigableSet)
NavigableSet<K> descendingKeySet();

//返回key升序排列的子映射,设置包含标志
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive);

//按key升序排列,返回子映射,开头到toKey,设置包含标志
NavigableMap<K,V> headMap(K toKey, boolean inclusive);

//按key升序排列,返回子映射,fromKey到末尾,设置包含标志
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);

//同时也继承了SortedMap的【不带包含标志】的子映射方法
SortedMap<K,V> subMap(K fromKey, K toKey);
SortedMap<K,V> headMap(K toKey);
SortedMap<K,V> tailMap(K fromKey);

1.3 ConcurrentNavigableMap方法

从定义上看,ConcurrentNavigableMap继承了NavigableMap和ConcurrentMap,就是两者功能的结合,既保证线程安全性,又提供导航搜索子Map视图的功能。ConcurrentNavigableMap中定义的方法也很简单,跟NavigableMap中的方法类似,只不过NavigableMap中返回的子Map视图是NavigableMap类型,而在ConcurrentNavigableMap中返回的都是ConcurrentNavigableMap类型。

// 返回此映射部分的视图,其键的范围从fromKey到toKey。
ConcurrentNavigableMap《K,V》 subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)

// 返回此映射部分的视图,其键的范围从fromKey(包含)到toKey(不包括)。
ConcurrentNavigableMap《K,V》 subMap(K fromKey, K toKey)

// 返回此映射部分的视图,其键严格小于toKey。
ConcurrentNavigableMap《K,V》 headMap(K toKey)

// 返回此映射的部分视图,其键小于(或等于,如果包含为true)toKey。
ConcurrentNavigableMap《K,V》 headMap(K toKey, boolean inclusive)

// 返回此映射的部分视图,其键大于或等于fromKey。
ConcurrentNavigableMap《K,V》 tailMap(K fromKey)

// 返回此映射部分的视图,其键大于(或等于,如果inclusive为true)fromKey。
ConcurrentNavigableMap《K,V》 tailMap(K fromKey, boolean inclusive)

// 返回此映射中包含的映射的逆序视图。
ConcurrentNavigableMap《K,V》 descendingMap()

// 返回此映射中包含的键的NavigableSet视图。
NavigableSet《K》 navigableKeySet()

// 返回此映射中包含的键的NavigableSet视图。
NavigableSet《K》 keySet()

// 返回此映射中包含的键的反向顺序NavigableSet视图。
NavigableSet《K》 descendingKeySet()

image-1656549432129

2、重点方法介绍

2.1 headMap()

headMap(T toKey)方法会返回一个key值小于给定的值的一个map集合。如果改变初始集合,这个改动也会映射到headMap中。示例如下:

import static org.junit.jupiter.api.Assertions.*;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.junit.jupiter.api.Test;

class ConcurrentNavigableMapTest {
 
    @Test
    void test() {
        ConcurrentNavigableMap map = new ConcurrentSkipListMap();
        map.put("1", "one");
        map.put("2", "two");
        map.put("3", "three");
        ConcurrentNavigableMap headMap = map.headMap("2");
        Set<String> keySet = headMap.keySet();
        for(String key : keySet) {
            System.out.println("key:" + key +" , value:" + headMap.get(key));
        }
    }
}

程序运行结果

headMap将指向只包含key为1的ConcurrentNavigableMap, 因为只有这个键严格小于“2”。

key:1 , value:one

2.2 tailMap()

tailMap(T fromKey) 方法返回一个key值大于和等于给定的值的一个map集合。如果改变初始集合,这个改动也会映射到tailMap中。示例如下:

import static org.junit.jupiter.api.Assertions.*;
import java.util.Set;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.junit.jupiter.api.Test;
 
class ConcurrentNavigableMapTest {
 
    @Test
    void test() {
        ConcurrentNavigableMap map = new ConcurrentSkipListMap();
        map.put("1", "one");
        map.put("2", "two");
        map.put("3", "three");
        ConcurrentNavigableMap headMap = map.tailMap("2");
        Set<String> keySet = headMap.keySet();
        for(String key : keySet) {
            System.out.println("key:" + key +" , value:" + headMap.get(key));
        }
    }
}

程序运行结果

tailMap包含了 keys “2” 和"3" ,因为这两个key大于等于给定的key”2”。

key:2 , value:two
 
key:3 , value:three

2.3 subMap()

subMap(K fromKey, K toKey)方法返回一个参数中从fromKey(包含)到tokey(不包含)的部分集合。示例如下:

import static org.junit.jupiter.api.Assertions.*;
import java.util.Set;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.junit.jupiter.api.Test;
 
class ConcurrentNavigableMapTest {
    @Test
    void test() {
        ConcurrentNavigableMap map = new ConcurrentSkipListMap();
        map.put("1", "one");
        map.put("2", "two");
        map.put("3", "three");
        ConcurrentNavigableMap headMap = map.subMap("2", "3");
        Set<String> keySet = headMap.keySet();
        for(String key : keySet) {
            System.out.println("key:" + key +" , value:" + headMap.get(key));
        }
    }
}

程序运行结果

返回的submap contains 只有 key为"2", 因为大于等于 “2” 小于 “3”。

key:2 , value:two

3、代码示例

3.1 ConcurrentNavigableMap示例1

import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
public class TestThread {
   public static void main(final String[] arguments) {
      ConcurrentNavigableMap<String,String> map =
         new ConcurrentSkipListMap<String, String>();
      map.put("1", "One");
      map.put("2", "Two");
      map.put("3", "Three");
      map.put("5", "Five");
      map.put("6", "Six");
      System.out.println("Initial ConcurrentHashMap: "+map);
      System.out.println("HeadMap(\"2\") of ConcurrentHashMap: "+map.headMap("2"));
      System.out.println("TailMap(\"2\") of ConcurrentHashMap: "+map.tailMap("2"));
      System.out.println(
         "SubMap(\"2\", \"4\") of ConcurrentHashMap: "+map.subMap("2","4"));
   }  
}

程序执行结果

Initial ConcurrentHashMap: {1 = One, 2 = Two, 3 = Three, 5 = Five, 6 = Six}
HeadMap("2") of ConcurrentHashMap: {1 = One}
TailMap("2") of ConcurrentHashMap: {2 = Two, 3 = Three, 5 = Five, 6 = Six}
SubMap("2", "4") of ConcurrentHashMap: {2 = Two, 3 = Three}

3.2 ConcurrentNavigableMap示例2

ConcurrentNavigableMap<Integer, String> concurrentNavigableMap = new ConcurrentSkipListMap();
    concurrentNavigableMap.put(1, "张三");
    concurrentNavigableMap.put(2, "李四");
    concurrentNavigableMap.put(3, "王五");
    concurrentNavigableMap.put(4, "赵六");

    //取key 1 - 4 之间的值 包头不报尾
    ConcurrentNavigableMap<Integer, String> subMap = concurrentNavigableMap.subMap(1, 4);
    System.out.println(JSON.toJSONString(subMap));

    //取传入key之前值
    ConcurrentNavigableMap<Integer, String> tailMap = concurrentNavigableMap.tailMap(2);
    System.out.println(JSON.toJSONString(tailMap));

    //取传入key之后的值
    ConcurrentNavigableMap<Integer, String> headMap = concurrentNavigableMap.headMap(3);
    System.out.println(JSON.toJSONString(headMap));

    //倒叙获取所有的key
    NavigableSet<Integer> navigableSet = concurrentNavigableMap.descendingKeySet();
    System.out.println(JSON.toJSONString(navigableSet));

    //正序获取所有的key
    NavigableSet<Integer> navigableKeySet = concurrentNavigableMap.navigableKeySet();
    System.out.println(JSON.toJSONString(navigableKeySet));

    //倒叙获取整个map
    ConcurrentNavigableMap<Integer, String> descendingMap = concurrentNavigableMap.descendingMap();
    System.out.println(JSON.toJSONString(descendingMap));

    //获取指定key 的一个map
    Map.Entry<Integer, String> ceilingEntry = concurrentNavigableMap.ceilingEntry(4);
    System.out.println(JSON.toJSONString(ceilingEntry));

    //获取指定key 的一个key
    Integer ceilingKey = concurrentNavigableMap.ceilingKey(4);
    System.out.println(JSON.toJSONString(ceilingKey));

    //与ceilingEntry 似乎对等
    Map.Entry<Integer, String> floorEntry = concurrentNavigableMap.floorEntry(4);
    System.out.println(JSON.toJSONString(floorEntry));

    //取比当前key大一个的map值
    Map.Entry<Integer, String> higherEntry = concurrentNavigableMap.higherEntry(2);
    System.out.println(JSON.toJSONString(higherEntry));

    //取比当前key小一个的map值
    Map.Entry<Integer, String> lowerEntry = concurrentNavigableMap.lowerEntry(2);
    System.out.println(JSON.toJSONString(lowerEntry));

    //取最后一个map值
    Map.Entry<Integer, String> lastEntry = concurrentNavigableMap.lastEntry();
    System.out.println(JSON.toJSONString(lastEntry));

    //删除并返回与此映射中最小键关联的键值映射,如果映射为空,则返回null
    /** Map.Entry<Integer, String> firstEntry = concurrentNavigableMap.pollFirstEntry();
    System.out.println(JSON.toJSONString(firstEntry));
    System.out.println(JSON.toJSONString(concurrentNavigableMap));*/

    //删除并返回与此映射中最大键关联的键值映射,如果映射为空,则返回 {@code null}。 @return 移除
    /*Map.Entry<Integer, String> pollLastEntry = concurrentNavigableMap.pollLastEntry();*/

    //可对响应的value 做计算
    /*String compute = concurrentNavigableMap.compute(2, new BiFunction<Integer, String, String>() {
        @Override
        public String apply(Integer integer, String s) {
            return integer + ":BiFunction";
        }
    });
    System.out.println(compute);*/

    //如果key 存在 返回原來的值 不存在返回 "99999999"
    String computeIfAbsent = concurrentNavigableMap.computeIfAbsent(100, new Function<Integer, String>() {
        @Override
        public String apply(Integer integer) {
            return "99999999";
        }
    });
    System.out.println(computeIfAbsent);

    //可以拼接map中的key和value
    String computeIfPresent = concurrentNavigableMap.computeIfPresent(2, new BiFunction<Integer, String, String>() {
        @Override
        public String apply(Integer integer, String s) {
            return integer + "=======" + s;
        }
    });
    System.out.println(computeIfPresent);
}

3.3 NavigableMap示例

public static void main(String[] args) {

        // NavigableMap多态接收TreeMap的实例
        NavigableMap<String, Integer> navigatorTreeMap = new TreeMap<String, Integer>() {{
            put("aa", 11);
            put("bb", 22);
            put("cc", 33);
            put("dd", 44);
            put("ee", 55);
            put("ff", 55);
            put("gg", 55);
        }};


        System.out.println(navigatorTreeMap.size());// 7个元素:7
        System.out.println(navigatorTreeMap.ceilingKey("cc"));// 返回大于等于cc的最小键:cc
        System.out.println(navigatorTreeMap.ceilingEntry("c"));//  返回一个键-值映射关系,它与大于等于cc的最小键关联:cc=33
        System.out.println(navigatorTreeMap.firstKey());// 最小键:aa
        System.out.println(navigatorTreeMap.firstEntry());// 最小键对应的k-v键值对:aa=11


        System.out.println(navigatorTreeMap.floorEntry("c"));// 返回一个键-值映射关系,它与小于等于给定键的最大键关联:bb=22
        System.out.println(navigatorTreeMap.floorKey("cc"));//   返回小于等于cc的最大键:cc
        System.out.println(navigatorTreeMap.headMap("bb"));// 返回此映射的部分视图,其键值严格小于bb:{aa=11}
        System.out.println(navigatorTreeMap.headMap("bb", true));// 同上小于等于(true):{aa=11, bb=22}
        System.out.println(navigatorTreeMap.higherEntry("c"));// 返回一个键-值映射关系,它与小于等于给定键的最大键关联:cc=33
        System.out.println(navigatorTreeMap.higherKey("cc"));//   返回小于等于cc的最大键:dd
        System.out.println(navigatorTreeMap.lastEntry());// 返回一个键-值映射关系,它与小于等于给定键的最大键关联:gg=55
        System.out.println(navigatorTreeMap.lastKey());//   返回小于等于cc的最大键:gg
        System.out.println(navigatorTreeMap.lowerEntry("c"));// 返回一个键-值映射关系,它与小于等于给定键的最大键关联:bb=22
        System.out.println(navigatorTreeMap.lowerKey("cc"));//    返回严格小于cc的最大键:bb
        System.out.println(navigatorTreeMap.pollFirstEntry());//  移除并返回与此映射中的最小键关联的键-值映射关系:aa=11
        System.out.println(navigatorTreeMap.pollLastEntry());//  移除并返回与此映射中的最大键关联的键-值映射关系:gg=55
        System.out.println(navigatorTreeMap.navigableKeySet());//   返回此映射中所包含键的
        // NavigableSet 视图。:[bb, cc, dd, ee, ff]

        System.out.println(navigatorTreeMap.subMap("aa", true, "dd", true));// 返回部分视图,true表示包括当前元素键值对:{bb=22, cc=33, dd=44}
        System.out.println(navigatorTreeMap.subMap("bb", "dd"));// 返回部分视图包括前面的元素,不包括后面的:{bb=22, cc=33}
        System.out.println(navigatorTreeMap.tailMap("cc"));// 返回元素大于cc的元素映射视图,包括cc://{cc=33, dd=44, ee=55, ff=55}
        System.out.println(navigatorTreeMap.tailMap("cc", false));// 返回元素大于等于cc的元素映射视图:{dd=44, ee=55, ff=55}

        //逆序视图
        NavigableMap<String, Integer> descendingMap = navigatorTreeMap.descendingMap();
        System.out.println(navigatorTreeMap); //原来的Map:
        System.out.println(descendingMap);// 返回逆序视图:{gg=55, ff=55, ee=55, dd=44, cc=33, bb=22, aa=11}
        //执行一个移除操作后  再看看会不会影响到原来的Map
        descendingMap.remove("gg");
        System.out.println(navigatorTreeMap); //原来的Map:{aa=11, bb=22, cc=33, dd=44, ee=55, ff=55}
        System.out.println(descendingMap);// 返回逆序视图:{ff=55, ee=55, dd=44, cc=33, bb=22, aa=11}
}
0

评论区