• java集合-TreeSet


    一:基本概念

    TreeSet基于 TreeMap 的 NavigableSet 实现。

    • 使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
    • 此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。
    • 如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator。)这是因为 Set 接口是按照 equals 操作定义的,但 TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也是 定义良好的;它只是违背了 Set 接口的常规协定。
    • TreeSet不是同步的。如果多个线程同时访问一个 TreeSet,而其中至少一个线程修改了该 set,那么它必须 外部同步。这一般是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSortedSet 方法来“包装”该 set。此操作最好在创建时进行,以防止对 set 的意外非同步访问: SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));
    • 此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果从结构上对 set 进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出 ConcurrentModificationException。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。
    • 注迭代器的快速失败行为无法得到保证,一般来说,存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。

    实现结构图如下:(来源互联网)

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

    这里写图片描述
    API 简单测试:

    package com.csu.collection;
    
    import java.util.Iterator;
    import java.util.TreeSet;
    
    
    public class TreeSetTest {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		testTreeSetAPIs();
    	}
    	
        public static void testTreeSetAPIs() {
            String val;
            TreeSet<String> tSet = new TreeSet<>();
            tSet.add("aaa");
            // Set中不允许重复元素,所以只会保存一个“aaa”
            tSet.add("aaa");
            tSet.add("bbb");
            tSet.add("eee");
            tSet.add("ddd");
            tSet.add("ccc");
            System.out.println("TreeSet:"+tSet);
            // 打印TreeSet的实际大小
            System.out.printf("size : %d
    ", tSet.size());
    
            // 导航方法
            // floor(小于、等于)
            System.out.printf("floor bbb: %s
    ", tSet.floor("bbb"));
            // lower(小于)
            System.out.printf("lower bbb: %s
    ", tSet.lower("bbb"));
            // ceiling(大于、等于)
            System.out.printf("ceiling bbb: %s
    ", tSet.ceiling("bbb"));
            System.out.printf("ceiling eee: %s
    ", tSet.ceiling("eee"));
            // ceiling(大于)
            System.out.printf("higher bbb: %s
    ", tSet.higher("bbb"));
            // subSet()
            System.out.printf("subSet(aaa, true, ccc, true): %s
    ", tSet.subSet("aaa", true, "ccc", true));
            System.out.printf("subSet(aaa, true, ccc, false): %s
    ", tSet.subSet("aaa", true, "ccc", false));
            System.out.printf("subSet(aaa, false, ccc, true): %s
    ", tSet.subSet("aaa", false, "ccc", true));
            System.out.printf("subSet(aaa, false, ccc, false): %s
    ", tSet.subSet("aaa", false, "ccc", false));
            // headSet()
            System.out.printf("headSet(ccc, true): %s
    ", tSet.headSet("ccc", true));
            System.out.printf("headSet(ccc, false): %s
    ", tSet.headSet("ccc", false));
            // tailSet()
            System.out.printf("tailSet(ccc, true): %s
    ", tSet.tailSet("ccc", true));
            System.out.printf("tailSet(ccc, false): %s
    ", tSet.tailSet("ccc", false));
            // 删除“ccc”
            tSet.remove("ccc");
            // 将Set转换为数组
            String[] arr = (String[])tSet.toArray(new String[0]);
            for (String str:arr)
                System.out.printf("for each : %s
    ", str);
    
            // 打印TreeSet
            System.out.printf("TreeSet:%s
    ", tSet);
    
            // 遍历TreeSet
            for(Iterator<String> iter = tSet.iterator(); iter.hasNext(); ) {
                System.out.printf("iter : %s
    ", iter.next());
            }
    
            // 删除并返回第一个元素
            val = (String)tSet.pollFirst();
            System.out.printf("pollFirst=%s, set=%s
    ", val, tSet);
    
            // 删除并返回最后一个元素
            val = (String)tSet.pollLast();
            System.out.printf("pollLast=%s, set=%s
    ", val, tSet);
    
            // 清空HashSet
            tSet.clear();
    
            // 输出HashSet是否为空
            System.out.printf("%s
    ", tSet.isEmpty()?"set is empty":"set is not empty");
        }
    
    }
    
    

    运行结果:

    TreeSet:[aaa, bbb, ccc, ddd, eee]
    size : 5
    floor bbb: bbb
    lower bbb: aaa
    ceiling bbb: bbb
    ceiling eee: eee
    higher bbb: ccc
    subSet(aaa, true, ccc, true): [aaa, bbb, ccc]
    subSet(aaa, true, ccc, false): [aaa, bbb]
    subSet(aaa, false, ccc, true): [bbb, ccc]
    subSet(aaa, false, ccc, false): [bbb]
    headSet(ccc, true): [aaa, bbb, ccc]
    headSet(ccc, false): [aaa, bbb]
    tailSet(ccc, true): [ccc, ddd, eee]
    tailSet(ccc, false): [ddd, eee]
    for each : aaa
    for each : bbb
    for each : ddd
    for each : eee
    TreeSet:[aaa, bbb, ddd, eee]
    iter : aaa
    iter : bbb
    iter : ddd
    iter : eee
    pollFirst=aaa, set=[bbb, ddd, eee]
    pollLast=eee, set=[bbb, ddd]
    set is empty
    

    二:源码分析

    1:变量定义:

    NavigableMap对象,后边的集合操作就是基于它来操作的。

    private transient NavigableMap<E,Object> m;
    

    2:构造函数

    public TreeSet() {
            this(new TreeMap<E,Object>());
        }
    

    创建一个空集,我们可以看出这个集合采用TreeMap来构造,以集合的值为Treemap的Key,因此集合不能存储重复的数据,这个构造函数使得集合排序按照TreeMap的自然顺序。

     public TreeSet(Comparator<? super E> comparator) {
            this(new TreeMap<>(comparator));
        }
    

    构造一个新的空 TreeSet,它根据指定比较器进行排序。也就是说,我们可以通过自己实现comparator接口来自定义排序。

    public TreeSet(Collection<? extends E> c) {
            this();
            addAll(c);
        }
    

    构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。我们可以看出通过调用addAll()方法将这个数据插入到集合中。

    public TreeSet(SortedSet<E> s) {
            this(s.comparator());
            addAll(s);
        }
    

    同样的道理,构造一个包含SortedSet的集合。
    3:主要方法分析:
    iterator():

    public Iterator<E> iterator() {
            return m.navigableKeySet().iterator();
        }
    

    我们可以看出TreeSet的迭代器是通过 NavigableMap对象的中KeySet方法获取的。
    contains():

    public boolean contains(Object o) {
            return m.containsKey(o);
        }
    

    调用的事map的containsKey()方法;
    add();

    public boolean add(E e) {
            return m.put(e, PRESENT)==null;
        }
    

    也是直接调用map的方法。
    addAll():

    public  boolean addAll(Collection<? extends E> c) {
            // Use linear-time version if applicable
            if (m.size()==0 && c.size() > 0 &&
                c instanceof SortedSet &&
                m instanceof TreeMap) {
                SortedSet<? extends E> set = (SortedSet<? extends E>) c;
                TreeMap<E,Object> map = (TreeMap<E, Object>) m;
                Comparator<?> cc = set.comparator();
                Comparator<? super E> mc = map.comparator();
                if (cc==mc || (cc != null && cc.equals(mc))) {               
                //还是通过调用TreeMap的方法添加元素
                    map.addAllForTreeSet(set, PRESENT);
                    return true;
                }
            }
            return super.addAll(c);
        }
    

    将指定 collection 中的所有元素添加到此 set 中。

    应用实例

    通TreeSet实现全排列
    测试端:

    mport java.util.Iterator;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class TreeSetTest1 {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
          Set<String> set=new TreeSet<>();
          char []arr={'a','c','c','d'};
          permutations(set, arr, 0, arr.length);
          Iterator<String> iterator=set.iterator();
          while(iterator.hasNext())
          {
        	  System.out.println(iterator.next());
          }
    	}
       
    	public static void swap(char[] arr,int x,int y)
    	{
    		char temp=arr[x];
    		arr[x]=arr[y];
    		arr[y]=temp;
    	}
    	
    	public static void permutations(Set<String> set,char[] arr,int start,int end)
    	{
    		if(start==end-1)
    		{
    			set.add(new String(arr));
    		}
    		for(int i=start;i<end;i++)
    		{
    			swap(arr, start, i);
    			permutations(set, arr, start+1, end);
    			swap(arr, start, i);
    		}
    	}
    }
    

    运行结果:

    accd
    acdc
    adcc
    cacd
    cadc
    ccad
    ccda
    cdac
    cdca
    dacc
    dcac
    dcca
    
  • 相关阅读:
    一个Windows的对话框
    怎样用Javascript定义一个类
    WPF 开源UI框架
    企业级系统的认证与授权设计
    MVC (ModelBinder等)技巧
    oracle 调用存储过程和方法
    WPF相关文章索引 (持续更新中)
    当文件操作遇上进程占用时
    每位设计师都应该拥有的50个CSS代码片段
    wpf 复制/剪切到本地系统剪切板中以供右键粘贴用
  • 原文地址:https://www.cnblogs.com/csuwater/p/5404491.html
Copyright © 2020-2023  润新知