day26--Java集合09
Java集合09
18.TreeSet
- 元素无序:插入顺序和输出顺序不一致
- 可以按照一定的规则进行排序,具体排序方式取决于构造方法:
- TreeSet () :根据其元素的自然排序进行排序
- TreeSet (Comparator comparator) :根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,不包含重复元素
[TreeSet集合的理解(自然排序和比较器排序)-CSDN博客]
例子:
package li.collection.set.treeset; import java.util.Comparator; import java.util.TreeSet; @SuppressWarnings("all") public class TreeSet_ { public static void main(String[] args) { //TreeSet treeSet = new TreeSet(); TreeSet treeSet = new TreeSet(new Comparator() {//匿名内部类 @Override public int compare(Object o1, Object o2) { //下面调用String的compareTo方法进行字符串的小的比较 return ((String)o2).compareTo((String)o1);//从大到小 } }); //添加数据 treeSet.add("lucy"); treeSet.add("bob"); treeSet.add("smith"); treeSet.add("join"); treeSet.add("mary"); treeSet.add("q"); System.out.println(treeSet);//[smith, q, mary, lucy, join, bob] } }
-
当我们使用无参构造器 创建TreeSet时,会有一个默认的比较器
TreeSet可以对集合中的元素进行排序,在添加元素的时候会自动去调用Comparable接口的compareTo方法
有些泛型类已经写好了排序规则,比如String 和 Integer 都已经实现了Comparable接口,也重写了compareTo方法
-
现在希望添加的元素按照字符串大小来排序
-
使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类),并指定排序规则
TreeSet treeSet = new TreeSet(new Comparator() {//匿名内部类 @Override public int compare(Object o1, Object o2) { //下面调用String的compareTo方法进行字符串的小的比较 return ((String)o2).compareTo((String)o1);//从大到小 } }); -
简单看看源码
4.1 构造器把传入的比较器对象付赋给了TreeSet底层TreeMap的属性this.comparator
4.2在调用
treeSet.add("lucy")
时,在底层会执行到:if (cpr != null) {//cpr就是我们的匿名内部类(对象) do { parent = t; cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类(对象)的compareTo方法 if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else//如果相等,即返回0,这个key就没有加入 return t.setValue(value); } while (t != null); }
思考:如果要求加入的元素按照长度大小排序该怎么写?
package li.collection.set.treeset; import java.util.Comparator; import java.util.TreeSet; @SuppressWarnings("all") public class TreeSet_ { public static void main(String[] args) { TreeSet treeSet = new TreeSet(new Comparator() {//匿名内部类 @Override public int compare(Object o1, Object o2) { //按照长度大小排序 return ((String)o2).length()-((String)o1).length();//长度从大到小 } }); treeSet.add("lucy"); treeSet.add("bob"); treeSet.add("q"); System.out.println(treeSet);//[lucy, bob, q] } }
问:如果此时再使用add()方法添加一个"jack"字符串,这个字符串可以添加进treeSet吗?

如上图,答案是不能。之前已经说过,在使用add()方法时,底层会调用:
if (cpr != null) {//cpr就是我们的匿名内部类(对象) do { parent = t; cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类(对象)的compareTo方法 if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else//如果相等,即返回0,这个key就没有加入 return t.setValue(value); } while (t != null); }
由于我们重写了compareTo方法,方法此时返回的是两个元素长度之差。
在do...while循环比较时,因为在加入“jack”之前已经有一个相同长度为4的字符串“lucy”,所以compareTo返回的值为0,即cmp=0,执行语句return t.setValue(value);
即 认为是同一个key,因此“jack”无法加入集合treeSet
19.TreeMap

例子:
package li.map.treemap; import java.util.Comparator; import java.util.TreeMap; @SuppressWarnings("all") public class TreeMap_ { public static void main(String[] args) { //使用默认的构造器穿件TreeMap,是无序的(也没有排序) //要求:按照传入的字符串(key)的大小进行排序 //TreeMap treeMap = new TreeMap(); TreeMap treeMap = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { //按照传入的字符串(key)的大小进行排序 //return ((String)o2).compareTo((String)o1); //按照key的字符串长度大小排序 return ((String)o2).length()-((String)o1).length(); } }); treeMap.put("jack","杰克"); treeMap.put("tom","汤姆"); treeMap.put("kristina","克里斯提诺"); treeMap.put("smith","史密斯"); System.out.println(treeMap);//按照key的长度排序 // {kristina=克里斯提诺, smith=史密斯, jack=杰克, tom=汤姆} } }
如下图:打上断点,点击debug,点击force step into进入到构造器中

- 把传入的实现了Comparator接口的匿名内部类(对象),传给 了TreeMap的comparator属性

-
接下来调用put方法:
public V put(K key, V value) { // 先以 t 保存链表的 root 节点 Entry<K,V> t = root; // 如果 t==null,表明是一个空链表,即该 TreeMap 里没有任何 Entry if (t == null) { // 将新的 key-value 创建一个 Entry,并将该 Entry 作为 root root = new Entry<K,V>(key, value, null); // 设置该 Map 集合的 size 为 1,代表包含一个 Entry size = 1; // 记录修改次数为 1 modCount++; return null; } int cmp; Entry<K,V> parent; Comparator<? super K> cpr = comparator; // 如果比较器 cpr 不为 null,即表明采用定制排序 if (cpr != null) { do { // 使用 parent 上次循环后的 t 所引用的 Entry parent = t; // 拿新插入 key 和 t 的 key 进行比较 cmp = cpr.compare(key, t.key); // 如果新插入的 key 小于 t 的 key,t 等于 t 的左边节点 if (cmp < 0) t = t.left; // 如果新插入的 key 大于 t 的 key,t 等于 t 的右边节点 else if (cmp > 0) t = t.right; // 如果两个 key 相等,新的 value 覆盖原有的 value, // 并返回原有的 value else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; do { // 使用 parent 上次循环后的 t 所引用的 Entry parent = t; // 拿新插入 key 和 t 的 key 进行比较 cmp = k.compareTo(t.key); // 如果新插入的 key 小于 t 的 key,t 等于 t 的左边节点 if (cmp < 0) t = t.left; // 如果新插入的 key 大于 t 的 key,t 等于 t 的右边节点 else if (cmp > 0) t = t.right; // 如果两个 key 相等,新的 value 覆盖原有的 value, // 并返回原有的 value else return t.setValue(value); } while (t != null); } // 将新插入的节点作为 parent 节点的子节点 Entry<K,V> e = new Entry<K,V>(key, value, parent); // 如果新插入 key 小于 parent 的 key,则 e 作为 parent 的左子节点 if (cmp < 0) parent.left = e; // 如果新插入 key 小于 parent 的 key,则 e 作为 parent 的右子节点 else parent.right = e; // 修复红黑树 fixAfterInsertion(e); size++; modCount++; return null; } 2.1第一次添加时,把 k-v 封装到Entry对象中,并放入root
// 先以 t 保存链表的 root 节点 Entry<K,V> t = root; // 如果 t==null,表明是一个空链表,即该 TreeMap 里没有任何 Entry if (t == null) { // 将新的 key-value 创建一个 Entry,并将该 Entry 作为 root root = new Entry<K,V>(key, value, null); // 设置该 Map 集合的 size 为 1,代表包含一个 Entry size = 1; // 记录修改次数为 1 modCount++; return null; } 2.2 之后的添加:
Comparator<? super K> cpr = comparator; // 如果比较器 cpr 不为 null,即表明采用定制排序 if (cpr != null) { do { // 使用 parent 上次循环后的 t 所引用的 Entry parent = t; // 拿新插入 key 和 t 的 key 进行比较 cmp = cpr.compare(key, t.key); // 如果新插入的 key 小于 t 的 key,t 等于 t 的左边节点 if (cmp < 0) t = t.left; // 如果新插入的 key 大于 t 的 key,t 等于 t 的右边节点 else if (cmp > 0) t = t.right; // 如果两个 key 相等,新的 value 覆盖原有的 value, // 并返回原有的 value else return t.setValue(value); } while (t != null); } 思考:重写了compareTo方法之后,现在比较的是key的长度。如果在treeMap集合中加入K-V,该key与集合中的某个key长度相同,结果会如何?
答案:key不会被替换,但是value值会被最新的替换
20.Collections工具类
20.1排序
- Collections是一个提供操作Set、List和Map等集合的工具类
- Collections中提供了一系列静态的方法,对集合元素进行排序、查询和修改等操作
-
排序操作:
-
reverse(List):反转List中元素的顺序
-
shuffle(List):对List集合元素进行随机排序
-
sort(List):根据元素的自然顺序对指定List集合元素进行升序排序
-
sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
-
swap(List,int,int):将制定List集合中的 i 处元素和 j 处元素进行交换
-
排序操作例子:
package li.collection.collectionskit; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @SuppressWarnings("all") public class Collections_ { public static void main(String[] args) { //创建一个ArrayList集合用于测试 List list = new ArrayList(); list.add("tom"); list.add("smith"); list.add("king"); list.add("milan"); //- reverse(List):反转List中元素的顺序 Collections.reverse(list); System.out.println("reverse:" + list);//[milan, king, smith, tom] //- shuffle(List):对List集合元素进行随机排序 Collections.shuffle(list); System.out.println("shuffle:" + list);//每一次输出的顺序都不一样 //- sort(List):根据元素的自然顺序对指定List集合元素进行升序排序 Collections.sort(list); System.out.println("自然排序后" + list);//自然排序是按照字符串的大小来排的 //- sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序 //指定排序规,例如希望按照字符串的长度大小来排序 Collections.sort(list, new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String) o1).length() - ((String) o2).length(); } }); //注意这里可以输出字符串长度相同的字符,因为是list,允许重复,不用比较key System.out.println("按字符串长度大小排序:" + list);//按照字符串长度大小排序:[tom, king, milan, smith] //- swap(List,int,int):将制定List集合中的 i 处元素和 j 处元素进行交换 Collections.swap(list,1,3 ); System.out.println("交换后的排序:"+list); } }

20.2查找、替换
- Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
- Object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素
- Object min(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最小元素
- int frequency(Collection,Object):返回指定集合中指定元素的出现次数
- void copy(List dest,List src):将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVal,Object oldVal,Object newVal):使用新值替换List对象的所有旧值
例子:
package li.collection.collectionskit; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @SuppressWarnings("all") public class Collections_ { public static void main(String[] args) { //创建一个ArrayList集合用于测试 List list = new ArrayList(); list.add("tom"); list.add("smith"); list.add("king"); list.add("milan"); //1. Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素 System.out.println("自然顺序最大元素:" + Collections.max(list));//自然顺序最大元素:tom //2. Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素 //比如返回长度最大的元素 Object maxObject = Collections.max(list, new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String) o1).length() - ((String) o2).length(); } }); System.out.println("长度最大的元素:"+maxObject);//长度最大的元素:smith //3. Object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素 System.out.println("自然顺序最小元素:" + Collections.min(list));//自然顺序最小元素:king //4. Object min(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最小元素 //比如返回长度最大的元素 Object minObject = Collections.min(list, new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String) o1).length() - ((String) o2).length(); } }); System.out.println("长度最小的元素:"+minObject);//长度最小的元素:tom //5. int frequency(Collection,Object):返回指定集合中指定元素的出现次数 System.out.println(""+Collections.frequency(list,"tom"));// 1 //6. void copy(List dest,List src):将src中的内容复制到dest中 //拷贝:注意如果要拷贝的集合大于新的集合,就会抛出异常--Source does not fit in dest //因此我们需要先给dest赋值,使元素个数和 list.suze()一样 ArrayList dest = new ArrayList(); for (int i = 0; i < list.size(); i++) { dest.add(""); } Collections.copy(dest,list); System.out.println("dest:"+dest);//dest:[tom, smith, king, milan] //7. boolean replaceAll(List list,Object oldVal,Object oldVal,Object newVal):使用新值替换List对象的所有旧值 //例如,如果集合中有tom,就替换成 汤姆 Collections.replaceAll(list,"tom","汤姆"); System.out.println("替换后:"+list);//替换后:替换后:[汤姆, smith, king, milan] } }

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!