java集合之TreeMap实现原理

硅谷探秘者 2039 0 0

java集合之TreeMap实现原理

        TreeMap集合的实现其实说简单也简单说复杂也复杂,说简单是因为TreeMap底层实现完全依靠红黑树这个数据结构,相比与HashMap来说TreeMap不用考虑hash表的扩容也不用考虑hash冲突,以及一些具有策略性的转换等(比如jdk8以上HashMap中链表长度大于等于8的时候,会自动转换为红黑树,所以HashMap的结构本身就包含TreeMap)。所以TreeMap相比于HashMap就简单许多。说复杂是因为红黑树的实现也并非我们想象的那么简单,甚至还有点难度。要想把它用自己的代码实现需要对二叉树或二叉排序树非常熟悉,包括对查找二叉树前驱节点后继节点的算法以及二叉树旋转算法等。

红黑树的实现原理

        关于红黑树的实现原理之前有一篇博客已经介绍过了,在此就不多叙述,否则的话估计本篇3/4都在描述红黑树的实现。红黑树的实现请参考:http://www.jiajiajia.club/official/weblog/yjw520/42

TreeMap的结构

        从源码可以看出TreeMap实现了Map接口并定义了一下几个重要属性

private transient Entry<K,V> root;
    /**
     * The number of entries in the tree
     */
    private transient int size = 0;

    /**
     * The number of structural modifications to the tree.
     */
private transient int modCount = 0;
    /**
     * The comparator used to maintain order in this tree map, or
     * null if it uses the natural ordering of its keys.
     *
     * @serial
     */
    private final Comparator<? super K> comparator;
  1. root属性,root是红黑树实现的根节点,树中所有节点的添加、查找、删除等操作都必须经过根节点而来。
  2. Size属性不用多说,就是集合中节点的个数。
  3. modCount属性是记录对树进行结构修改的次数。
  4. comparator属性是一个比较器,用于解决TreeMap中key的排序问题,由于红黑树也是二叉查找树,它们当中每一个节点的比较值都必须大于或等于在它的左子树中的所有节点,并且小于或等于在它的右子树中的所有节点。这确保红黑树运作时能够快速的在树中查找给定的值。所以在调用put方法的key类必须实现Comparable接口或者在创建TreeMap的时候传入一个比较器Comparator的实现类。如下面两种实现:
class Entity implements Comparable<Entity>{
	int a;
	public Entity(int a) { this.a=a; }
	@Override
	public int compareTo(Entity o) {
		if(o.a>this.a) return 1;
		if(o.a<this.a) return -1;
		else return 0;
	}
}
public class Test {
	public static void main(String[] args) {
	    TreeMap<Entity, String> t=new TreeMap<Entity, String>();
	    t.put(new Entity(2),"2");
	    t.put(new Entity(3),"3");
	}
}
class Entity {
	public Entity(int a) { this.a=a; }
	int a;
}
public class Test {
	public static void main(String[] args) {
	    TreeMap<Entity, String> t=new TreeMap<Entity, String>(new Comparator<Entity>() {
			@Override
			public int compare(Entity o1, Entity o2) {
				if(o1.a>o2.a) return 1;
				if(o1.a<o2.a) return -1;
				else return 1;
			}
		});
	    t.put(new Entity(2),"2");
	    t.put(new Entity(3),"3");
	    System.out.println(t);
	}
}

        如果你使用HashMap的时候不满足以上任何一种则会包java.lang.ClassCastException异常。(注意如果用Integer或String等不会有异常,这些类已经实现的Comparable接口)

TreeMap的两种遍历方式
public class Test {
	public static void main(String[] args) {
	    TreeMap<Integer, Integer> t=new TreeMap<Integer, Integer>();
	    t.put(2,2);
	    t.put(3,3);
	    t.put(9,9);
	    t.put(4,4);
	    t.put(5,5);
	    t.put(0,0);
	    t.put(8,8);
	    t.put(7,7);
	    t.put(1,1);
	    t.put(34,34);
	    t.put(90,90);
	    /**
	     * 	方式1
	     */
	    Iterator<Entry<Integer, Integer>> iterator = t.entrySet().iterator();
	    while (iterator.hasNext()) {
	    	Entry<Integer, Integer> entry =iterator.next();
	    	System.out.println(entry);
	    }
	    
	    System.out.println("****************************");
	    /**
	     * 	方式2
	     */
	    for(Entry<Integer, Integer> entry : t.entrySet()) {
	    	 System.out.println(entry);
	    }
	}
}
遍历TreeMap时删除元素的正确方式

        在遍历TreeMap时删除元素,一下两种方式会抛java.util.ConcurrentModificationException异常,所以做这个操作时一定要注意。

public class Test {
	public static void main(String[] args) {
	    TreeMap<Integer, Integer> t=new TreeMap<Integer, Integer>();
	    t.put(2,2);
	    t.put(3,3);
	    t.put(9,9);
	    t.put(4,4);
	    t.put(5,5);
	    t.put(0,0);
	    t.put(8,8);
	    t.put(7,7);
	    t.put(1,1);
	    t.put(34,34);
	    t.put(90,90);
	    Iterator<Entry<Integer, Integer>> iterator = t.entrySet().iterator();
	    while (iterator.hasNext()) {
	    	Entry<Integer, Integer> entry =iterator.next();
	    	System.out.println(entry);
	    	if(entry.getValue()==4) {
	    		t.remove(entry.getKey());//删除元素
	    	}
	    }
	    System.out.println("****************************");
	    for(Entry<Integer, Integer> entry : t.entrySet()) {
	    	 System.out.println(entry);
	    	 if(entry.getKey()==7) {
	    		t.remove(entry.getKey()); //删除元素
	    	 }
	    }
	}
}

正确的删除方式

public class Test {
	public static void main(String[] args) {
	    TreeMap<Integer, Integer> t=new TreeMap<Integer, Integer>();
	    t.put(2,2);
	    t.put(3,3);
	    t.put(9,9);
	    t.put(4,4);
	    t.put(5,5);
	    t.put(0,0);
	    t.put(8,8);
	    t.put(7,7);
	    t.put(1,1);
	    t.put(34,34);
	    t.put(90,90);
	    Iterator<Entry<Integer, Integer>> iterator = t.entrySet().iterator();
	    while (iterator.hasNext()) {
	    	Entry<Integer, Integer> entry =iterator.next();
	    	System.out.println(entry);
	    	if(entry.getValue()==4) {
	    		iterator.remove();//正确删除元素的方式
	    	}
	    }
	    System.out.println("****************************");
	    for(Entry<Integer, Integer> entry : t.entrySet()) {
	    	 System.out.println(entry);
	    }
	}
}
非线程安全

        TreeMap和HashMap一样都是非线程安全的,从它的源码的实现中可以看出,所以在并发编程中使用的时候要注意这一点。


评论区
请写下您的评论...
暂无评论...
猜你喜欢
java基础 1665 javaHashtable一、构造方法1.publicHashtable()publicHashtable(){this(11,0.75f);}无参构造,初始化一个容量为11,负载因子为
java基础 1378 1.HashMap的构造函数1.publicHashMap()publicHashMap(){this.loadFactor=DEFAULT_LOAD_FACTOR;//allotherfieldsdefaulted}构造一个空的HashMap,初始容量为16,负载因子为0.75,负载因子和它的作用将会在下方解释。2.publicHashMap(intinitialCapacity)publicH
java基础 1514 一、HashSet底层从HashSet的源码中可以发,它的底层维护了一个HashMap,在newHashSet的时候,构造方法中其是new了一个HashMap
java基础 2397 1.linkedList的linkedlist的是基于数据结构:双向链表。模型图如对于linkedList没有像arrayList一样有扩容的概念。也不需要移动数据。所以对于新增和删除操作
java基础 2895 ArrayList的是一个动态数组,从源码中也可以看出。这就决定了他的查找效率高,可以直接通过下标访问。但是对于删除和增加元素,需要大量的移动元素,所以插入和删除的效率就很低。ArrayList
算法基础 1878 Java中遍历的方式以list为例,有三种遍历方式。ListStringlist=newArrayList(); list.add("2"); list.add("2"); list.add
ASM,java基础 1256 /134cglib动态代底层分析java:http://www.jiajiajia.club/official/weblog/yjw520/34cglib代常用接口和api:http://www.jiajiaj
java基础 2896 Java框架数据结构是以某种形式将数据组织在一起的,它不仅存储数据,还支持访问和处数据的操作。Java提供了几个能有效地组织和操作数据的数据结构,这些数据结构通常称为Java框架。在平
归档
2018-11  12 2018-12  33 2019-01  28 2019-02  28 2019-03  32 2019-04  27 2019-05  33 2019-06  6 2019-07  12 2019-08  12 2019-09  21 2019-10  8 2019-11  15 2019-12  25 2020-01  9 2020-02  5 2020-03  16 2020-04  4 2020-06  1 2020-07  7 2020-08  13 2020-09  9 2020-10  5 2020-12  3 2021-01  1 2021-02  5 2021-03  7 2021-04  4 2021-05  4 2021-06  1 2021-07  7 2021-08  2 2021-09  8 2021-10  9 2021-11  16 2021-12  14 2022-01  7 2022-05  1 2022-08  3 2022-09  2 2022-10  2 2022-12  5 2023-01  3 2023-02  1 2023-03  4 2023-04  2 2023-06  3 2023-07  4 2023-08  1 2023-10  1 2024-02  1 2024-03  1 2024-04  1 2024-08  1
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo vue 导入导出 软件使用 idea插件 协议 无聊的知识 jenkins springboot mqtt协议 keepalived minio mysql ensp 网络基础 xxl-job rabbitmq haproxy srs 音视频 webrtc javascript 加密算法
目录
没有一个冬天不可逾越,没有一个春天不会来临。最慢的步伐不是跬步,而是徘徊,最快的脚步不是冲刺,而是坚持。