Set子接口与list子接口的不同,那就是list子接口是可以保存重复的数据的,而Set子接口是不能保存重复的数据的。
首先我们知道List集合排序方式直接使用Collections工具类中的sort方法中默认是升序
sort方法有两种重载的形式:
sort(List
要求传入的待排序容器中存放的对象所属的类必须实现Comparable接口以实现元素的比较。
sort(List
不强制要求待排序容器中存放的对象所属的类必须实现Comparable接口,但是要求传入第二个参数,这个参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法。
Comparable:内部比较器(默认比较器)
重写compareTo(Object t1,Object t2); 内部【默认】比较规则
排序的规则,就是compare方法中的代码;
t1-t2 升序(自然顺序)
t2-t1 降序(倒叙)
Comparator:外部比较器
重写int compare(T t1, T t2) 【自定义】的比较规则
t1-t2 升序(自然顺序)
t2-t1 降序(倒叙)
【外部比较器的优先级高于内部比较器】
TreeMap采用一种被称为“红黑树”的排序二叉树来保存Map中的的每个Entry——每个Entry都被当做红黑树的一个节点来对待;
TreeMap的插入就是一个“排序二叉树”算法:每当程序添加新节点时,总是从树的根节点开始比较,即将根节点当成当前节点,如果新增节点大于当前节点且当前节点的右节点存在,则以右节点作为当前节点;如果新增节点小于当前节点且当前节点的左节点存在,则以左节点作为当前节点;如果新增节点等于当前节点,则新增节点覆盖当前节点;直到某个节点的左右节点不存在,并结束循环;将新增的节点作为该节点的子节点,如果新增的节点大于该节点,则添加成该节点的右节点,如果新增的节点小于该节点,则添加成该节点的左节点;TreeMap 根据Key来获取value。
根据键值大小 默认方式给存入的元素进行升序排序
TreeMapmap=new TreeMap<>(); map.put(1,34); map.put(4,24); map.put(2,94); map.put(7,54); map.put(4,1); //去重了和根据键值进行默认升序排序 System.out.println(map);//{1=34, 2=94, 4=1, 7=54}
利用TreeMap的有参构造方法实现排序 外部比较器重写Comparator方法 返回1 实现了存取顺序一致且不去重 TreeMapmap=new TreeMap<>(new Comparator () { @Override public int compare(Integer o1, Integer o2) { //实现了 存取顺序一致 // return 1; //{1=34, 4=24, 2=94, 7=54, 4=1} //实现了 存取顺序相反 return -1; //{4=1, 7=54, 2=94, 4=24, 1=34} } }); map.put(1,34); map.put(4,24); map.put(2,94); map.put(7,54); map.put(4,1); //不去重了和存取顺序一致 System.out.println(map);
外部比较器重写Comparator方法 不去重且实现升序或者降序排列
TreeMapmap=new TreeMap<>(new Comparator () { @Override public int compare(Integer o1, Integer o2) { //o1-o1=0的话返回0 代表不存入树结构中 这是就删除了重复的元素 也是去重的原理 // 所以我们如同需要重复元素我们必须让他不返回0 返回其他即可 //若不等于0 则返回o1.compareTo(o2) 升序 //若不等于0 则返回o2.compareTo(o1) 降序 // return o1-o2==0?1:o2.compareTo(o1);// {7=54, 4=24, 4=1, 2=94, 1=34} // return o1-o2==0?1:o1.compareTo(o2);// {1=34, 2=94, 4=24, 4=1, 7=54} } }); map.put(1,34); map.put(4,24); map.put(2,94); map.put(7,54); map.put(4,1); //不去重了和存取顺序一致 System.out.println(map);
底层原理:
实际上TreeSet还是用TreeMap来保存set元素。所以原理和上面介绍的TreeMap一致
只是Set 是单列集合 Map 是双列集合。
1、HashSet与TreeSet接口的一点不同,HashSet 保存的数据是无序的,TreeSet保存的数据是有序的,所以如果要想保存的数据有序应该使用TreeSet子类。
2、利用TreeSet保存自定义类对象的时候,自定义所在的类一定要实现Comparable接口,如果没有实现这个接口那么就无法区分大小关系,而且在TreeSet中如果要进行排序,那么就要将所有的字段都进行比较,就是说在TreeSet中是依靠comparato()方法返回的是不是0来判断是不是重复元素的。
3、如果是HashSet子类,那么其判断重复数据的方式不是依靠的comparable接口而是Object类之中的两个方法:(1)取得对象的哈希码 hashCode();(2)对象比较:equals(); 这俩个方法均不需要自己编写,在eclipse里面可以使用右键source 选择自动生成。就像生成Getter 和Setter 方法一样。
TreeSettreeSet =new TreeSet<>(new Comparator () { @Override public int compare(Integer o1, Integer o2) { //o1-o1=0的话返回0 代表不存入树结构中 这是就删除了重复的元素 也是去重的原理 // 所以我们如同需要重复元素我们必须让他不返回0 返回其他即可 //若不等于0 则返回o1.compareTo(o2) 升序 //若不等于0 则返回o2.compareTo(o1) 降序 //return o1-o2==0?1:o1.compareTo(o2);//[67, 45, 34, 34, 12, 11, 9] return o1-o2==0?1:o2.compareTo(o1); //[9, 11, 12, 34, 34, 45, 67] } }); Collections.addAll(treeSet,12,34,11,45,34,67,9); System.out.println(treeSet);
总结:TreeSet 依靠的是Comparable 来区分重复数据;
HashSet 依靠的是hashCode()、equals()来区分重复数据
Set 里面不允许保存重复数据