源码分析——TreeSet

TreeSet作为Set接口实现类中的最后一个,它的原理其实也和HashSet、LinkedHashSet差不多,它主要是建立在TreeMap之上。

先继续熟悉它所处的结构体系:

从上图可以看出TreeSet和HashSet属于同一层级,唯一的区别是它可以对容器内的元素进行一定规则的排序。

继续看其继承体系:

 TreeSet与HashSet继承自同一个抽象类,即Abstract类。

public class TreeSet<E> extends AbstractSet<E> 
		implements NavigableSet<E>, Cloneable, java.io.Serializable

从上可以看出TreeSet并没有直接实现Set接口,而是和TreeMap类似,实现了NavigableSet接口,那么来看看该接口的主要提供方法:

public interface NavigableSet<E> extends SortedSet<E> {
    E lower(E e);
    //获得小于e的最大元素
    E floor(E e);
    //获得不大于e的最大元素
    E ceiling(E e);
    //获得不小于e的最小元素
    E higher(E e);
    //获得大于e的最小元素
    E pollFirst();
    //获得并删除第一个元素
    E pollLast();
    //获得并删除最后一个元素
    Iterator<E> iterator();
    //获得迭代器
    NavigableSet<E> descendingSet();
    //获得逆序的Set
    Iterator<E> descendingIterator();
    //获得逆序的Set迭代器
    NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                           E toElement,   boolean toInclusive);
    //获得子Set
    NavigableSet<E> headSet(E toElement, boolean inclusive);
    //获得首部子Set
    NavigableSet<E> tailSet(E fromElement, boolean inclusive);
    //获得尾部子Set
    SortedSet<E> subSet(E fromElement, E toElement);
    //获得SortedSet类型的子Set
    SortedSet<E> headSet(E toElement);
    //获得SortedSet类型的首部子Set
    SortedSet<E> tailSet(E fromElement);
    //获得SortedSet类型的尾部子Set
}

public interface SortedSet<E> extends Set<E> {
    Comparator<? super E> comparator();
    //获得比较器
    SortedSet<E> subSet(E fromElement, E toElement);
    //获得子Set
    SortedSet<E> headSet(E toElement);
    //获得首部子Set
    SortedSet<E> tailSet(E fromElement);
    //获得尾部子Set
    @Override
    default Spliterator<E> spliterator() {
    //获得分割器
        return new Spliterators.IteratorSpliterator<E>(
                this, Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED) {
            @Override
            public Comparator<? super E> getComparator() {
                return SortedSet.this.comparator();
            }
        };
    }
}

从上面代码中可以看出,NavigableSet和NavigableMap类似,都是对于边界值以及子Set进行处理的方法,而SortedSet和SortedMap类似,属于拥有比较其的Set。

接下来继续看其成员变量:

private transient NavigableMap<E,Object> m;
//私有成员变量,不可序列化,一个导航map,map中键值对分别为E和Object
		
private static final Object PRESENT = new Object();
//私有静态常量,当前对象,即用于填充键值对中的value值,不使用

也许看到这里有些疑惑,不是说TreeSet是建立在TreeMap上面么,为什么成员变量中没有TreeMap而只有一个NavigableMap。这里绕了一下,NavigableMap只是一个接口,因此此成员变量应该是接口的实例变量,而NavigableMap接口的实现类就是TreeMap。剩下的PRESENT就不用再多说了。

接下来看一看构造器:

TreeSet(NavigableMap<E,Object> m){
	//构造器1,传入map类型的集合
	this.m = m;
}

public TreeSet(){
	//构造器2,空参构造器,调用第一个构造器,传入默认的map集合
	this(new TreeMap<E,Object>());
}

public TreeSet(Comparator<? super E> comparator){
	//构造器3,传入一个比较器,此时先创建一个使用传入该比较器的
	//TreeMap集合,再将该集合作为参数传入第一个构造器中完成构造
	this(new TreeMap<>(comparator));
}

public TreeSet(Collection<? extends E> c){
	//构造器4,传入一个集合c,先调用第一个构造器,在将集合中
	//的元素复制到TreeSet中 
	this();
	addAll(c);
}

public TreeSet(SortedSet<E> s){
	//构造器5,传入一个排好序的Set集合,先获得s的比较器,再通过比较器
	//创建TreeSet,再将s中的内容复制到TreeSet中
	this.(s.comparator);
	addAll(s);
}

TreeSet有五个构造器,当传入的是同一种的NavigableMap<e,Object>的接口实现类时,则直接将其引用赋给m就完成了;当传入的是比较器,则创建一个该比较器的TreeMap,再调用本类构造器将TreeMap传入,将其引用赋给m;当传入空参时,则创建一个无比较器的TreeMap,再调用本类构造器将TreeMap传入并将其引用赋给m;当传入的是一个普通的集合c,则先调用本类构造器创建一个无比较器的TreeMap,再将c中的元素依次复制到TreeMap中;当传入的是一个SortedSet集合s时,先用s的比较器创建一个相同比较器的TreeMap,再将s中的元素依次复制到TreeMap中。

下面再来看看常用方法:

1.add

public boolean add(E e){
	//向TreeSet中增加一个元素,调用m中的增加方法,由于map
	//中是不允许key值重复的,所以TreeSet也不允许元素重复
	return m.put(e,PRESENT) == null;
}

同样,调用TreeMap中的put方法将元素e加入到键值对中的key中,但是和HashMap不同的时,如果传入的为null会抛出空指针异常。

2.remove

public boolean remove(Object o){
	//删除TreeSet中的某一个元素,调用m中的删除方法
	return m.remove(o) == PRESENT;
}

同理,删除方法

3.contains

public boolean contains(Object o){
	//判断TreeSet中是否含有某一个元素
	return m.containsKey(o);
}

判断TreeSet中是否含有某一个元素

4.size

public int size(){
	//获得TreeSet的元素数量,即m中的结点数量
	return m.size();
}

获取TreeSet中的元素个数。

5.isEmpty

public boolean isEmpty(){
	//判断TreeSet是否为空,即判断m是否为空
	return m.isEmpty();
}

判断TreeSet中是否为空

6.clear

public void clear(){
	//清空TreeSet
	m.clear();
}

清空TreeSet

7.获取某一个元素

public E first(){
	//获得第一个元素
	return m.firstKey();
}

public E last(){
	//获得最后一个元素
	return m.lastKey();
}

public E pollFirst(){
	//获得第一个元素
	Map.Entry<E,?> e = m.pollFirstEntry();
	return (e == null) ? null : e.getKey();
}

public E pollLast(){
	//获得最后一个元素
	Map.Enrty<E,?> e = m.pollLastEntry();
	return (e == null) ? null : e.getKey();
}

8.获取集合

public Iterator<E> iterator(){
	//获得迭代器
	return m.navigableKeySet().iterator();
}

public Iterator<E> descendingIterator(){
	//获得逆序迭代器
	return m.descendingKeySet().iterator();
}

public NavigableSet<E> descendingSet(){
	//获得逆序的Set集合
	return new TreeSet<>(m.descendingMap());
}

 

总结:

异同HashSetTreeSet
底层实现建立在HashMap上,散列表+链表+红黑树建立在TreeMap上,红黑树
是否有序无序有序,可以自定义顺序或者采用元素本身的顺序
线程安全不安全不安全
元素可为null元素可以是null元素不可为null,否则会抛出空指针异常
效率效率较低效率较高

总源码

public class TreeSet<E> extends AbstractSet<E> 
		implements NavigableSet<E>, Cloneable, java.io.Serializable
	{	//此TreeSet集合主要是建立在TreeMap集合的基础上的,使用其中键值对中的
		//key值用与存储元素,将value值都默认为Object类PRESENT,不予使用
	
		private transient NavigableMap<E,Object> m;
		//私有成员变量,不可序列化,一个导航map,map中键值对分别为E和Object
		
		private static final Object PRESENT = new Object();
		//私有静态常量,当前对象,即用于填充键值对中的value值,不使用
		
		TreeSet(NavigableMap<E,Object> m){
			//构造器1,传入map类型的集合
			this.m = m;
		}

		public TreeSet(){
			//构造器2,空参构造器,调用第一个构造器,传入默认的map集合
			this(new TreeMap<E,Object>());
		}

		public TreeSet(Comparator<? super E> comparator){
			//构造器3,传入一个比较器,此时先创建一个使用传入该比较器的
			//TreeMap集合,再将该集合作为参数传入第一个构造器中完成构造
			this(new TreeMap<>(comparator));
		}

		public TreeSet(Collection<? extends E> c){
			//构造器4,传入一个集合c,先调用第一个构造器,在将集合中
			//的元素复制到TreeSet中 
			this();
			addAll(c);
		}

		public TreeSet(SortedSet<E> s){
			//构造器5,传入一个排好序的Set集合,先获得s的比较器,再通过比较器
			//创建TreeSet,再将s中的内容复制到TreeSet中
			this.(s.comparator);
			addAll(s);
		}
		
		public Iterator<E> iterator(){
			//获得迭代器
			return m.navigableKeySet().iterator();
		}

		public Iterator<E> descendingIterator(){
			//获得逆序迭代器
			return m.descendingKeySet().iterator();
		}

		public NavigableSet<E> descendingSet(){
			//获得逆序的Set集合
			return new TreeSet<>(m.descendingMap());
		}
		
		public int size(){
			//获得TreeSet的元素数量,即m中的结点数量
			return m.size();
		}
		
		public boolean isEmpty(){
			//判断TreeSet是否为空,即判断m是否为空
			return m.isEmpty();
		}
		
		public boolean contains(Object o){
			//判断TreeSet中是否含有某一个元素
			return m.containsKey(o);
		}
		
		public boolean add(E e){
			//向TreeSet中增加一个元素,调用m中的增加方法,由于map
			//中是不允许key值重复的,所以TreeSet也不允许元素重复
			return m.put(e,PRESENT) == null;
		}
		
		public boolean remove(Object o){
			//删除TreeSet中的某一个元素,调用m中的删除方法
			return m.remove(o) == PRESENT;
		}
		
		public void clear(){
			//清空TreeSet
			m.clear();
		}
		
		public boolean addAll(Collection<? extends E> c){
			if(m.size() == 0 && c.size() > 0 && c instanceof SortedSet && m instanceof TreeMap){
				//首先判断m中是否是空的,c中是否有元素,c是否属于SortedSet体系中,m是否属于TreeMap体系中
				SortedSet<? extends E> set = (SortedSet<? extends E>) c;
				//先将c强制类型转换为SotredSet类型的Set集合set
				TreeMap<E,Object> map = (TreeMap<E,Object>) m;
				//再将m强制类型转换为TreeMap类型的map集合map
				Comparator<?> cc = set.comparator();
				//再获得set的比较器cc
				Comparator<? super E> mc = map.comparator();
				//获得map的比较器Mc
				if(cc == mc || (cc != null && cc.equals(mc))){
					//如果两个比较器相同,则调用Map中的建树方法将
					//set中的元素-默认value值全都建成一颗红黑树
					map.addAllForTreeSet(set,PRESENT);
					return true;
					//如果成功,则返回true
				}
			}
			return super.addAll(c);
			//否则调用父类方法进行增加
		}
		
		public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive){
			//获得子Set,返回调用m的subMap方法
			return new TreeSet<>(m.subMap(fromElement,fromInclusive,toElement,toInclusive));
		}
		
		public NavigableSet<E> headSet(E toElement, boolean inclusive){
			//获得首部子Set
			return new TreeSet<>(m.headSet(toElement,inclusive));
		}
		
		public NavigableSet<E> tailSet(E fromElement, boolean inclusive){
			//获得尾部子Set
			return new TreeSet<>(m.tailMap(fromElement,inclusive));
		}
		
		public SortedSet<E> subSet(E fromElement, E toElement){
			//获得子Set,默认包含开始元素,不包括结束元素
			return subSet(fromElement,true,toElement,false);
		}
		
		public SortedSet<E> headSet(E toElement){
			//获得首部子Set,默认不包括结束元素
			return headSet(toElement,false);
		} 
		
		public SortedSet<E> tailSet(E fromElement){
			//获得尾部子Set,默认包括开始元素
			return tailSet(fromElement,true);
		}
		
		public Comparator<? super E> comparator(){
			//获得TreeSet中的比较器
			return m.comparator();
		}
		
		public E first(){
			//获得第一个元素
			return m.firstKey();
		}

		public E last(){
			//获得最后一个元素
			return m.lastKey();
		}
		
		public E lower(E e){
			//获得小于传入元素e的最大元素、
			return m.lowerKey(e);
		}
		
		public E floor(E e){
			//获得不大于e的最大元素
			return m.floorKey(e);
		}
		
		public E ceiling(E e){
			//获得不小于e的最小元素
			return m.ceilingKey(e);
		}
		
		public E higher(E e){
			//获得大于e的最小元素
			return m.higherKey(e);
		}
		
		public E pollFirst(){
			//获得第一个元素
			Map.Entry<E,?> e = m.pollFirstEntry();
			return (e == null) ? null : e.getKey();
		}

		public E pollLast(){
			//获得最后一个元素
			Map.Enrty<E,?> e = m.pollLastEntry();
			return (e == null) ? null : e.getKey();
		}
		
		@SuppressWarnings("unchecked")
		public Object clone(){
			//克隆TreeSet
			TreeSet<E> clone;
			try{
				clone = (TreeSet<E>)super.clone();
			}catch(CloneNotSupportedException e){
				throw new InternalError(e);
			}
			clone.m = new TreeMap<>(m);
			return clone;
		}
		
		private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
			//写出操作
			s.defaultWriteObject();
			//写出其他隐藏的东西
			s.writeObject(m.comparator);
			//写出比较器
			s.writeInt(m.size());
			//写出TreeSet中的元素数量
			for(E e : m.keySet())
				s.writeObject(e);
			//遍历操作,写出所有的元素
		}
		
		private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException{
			//读入操作
			s.defaultReadObject();
			//读入其他隐藏的东西
			@SuppressWarnings("unchecked")
				Comparator<? super E> c = (Comparator<? super E>)s.readObject();
				//读入比较器
			TreeMap<E,Object> tm = new TreeMap<>(c);
			//创建一个新的TreeMap类
			m = tm;
			int size = s.readInt();
			//读入TreeSet的大小
			tm.readTreeSet(size,s,PRESENT);
			//调用tm的方法读入元素并建树
		}
		
		public Spliterator<E> spliterator(){
			//获得分割器
			return TreeMap.keySpliteratorFor(m);
		}
		
		private static final long serialVersionUID = -2479143000061671589L;
		//序列化标志,用于反序列化
	}	

参考资料:

https://blog.csdn.net/zuochao_2013/article/details/82734289

https://www.cnblogs.com/skywang12345/p/3311252.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值