热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java的泛型set接口

目录InterfaceSetSet接口HashSetLinkedHashSetTreeSetTreeSet存储对学生对象遍历InterfaceSet观察API文

目录

Interface Set

Set接口

HashSet

LinkedHashSet

TreeSet

TreeSet存储对学生对象遍历



Interface Set

观察API文档我们发现:

 public interface Set

extends Collection

不包含重复元素的集合。 更正式地,集合不包含一对元素e1e2 ,使得e1.equals(e2) ,并且最多一个空元素。 正如其名称所暗示的那样,这个接口模拟了数学抽象。

Set接口除了继承自Collection接口的所有构造函数的合同以及add,equals和hashCode方法的合同外 , 还 增加了其他规定。 其他继承方法的声明也包括在这里以方便。 (伴随这些声明的规范已经量身定做Set接口,但它们不包含任何附加的规定。)

构造函数的额外规定并不奇怪,所有构造函数都必须创建一个不包含重复元素的集合(如上所定义)。

注意:如果可变对象用作设置元素,则必须非常小心。 如果对象的值以影响equals比较的方式更改,而对象是集合中的元素, 则不指定集合的行为。 这种禁止的一个特殊情况是,一个集合不允许将其本身作为一个元素。

一些集合实现对它们可能包含的元素有限制。 例如,一些实现禁止空元素,有些实现对元素的类型有限制。 尝试添加不合格元素会引发未经检查的异常,通常为NullPointerException或ClassCastException 。 尝试查询不合格元素的存在可能会引发异常,或者可能只是返回false; 一些实现将展现出前者的行为,一些实现将展现出后者。 更一般来说,尝试对不符合条件的元素的操作,其完成不会导致不合格元素插入到集合中,可能会导致异常,或者可能会成功执行该选项。 此异常在此接口的规范中标记为“可选”。

此接口是成员Java Collections Framework 。




Set接口

一个不包含重复元素的 collection,就是元素唯一且元素无序(存储和取出不一致)的集合。

存储字符串并遍历,参考代码:

/*
存储字符串并遍历*/
import java.util.HashSet;
public class SetDemo {public static void main(String[] args) {//定义字符串集合HashSet arr &#61; new HashSet<>();//添加元素到集合arr.add("hello");arr.add("world");arr.add("java");arr.add("bigdata");arr.add("hadoop");arr.add("hello");arr.add("hello");arr.add("java");arr.add("spark");arr.add("flink");arr.add("world");arr.add("hadoop");for (String s : arr) {System.out.println(s);}}
}

输出结果&#xff1a;

flink
world
java
bigdata
spark
hello
hadoop




HashSet

查看API文档我们知道&#xff1a;

public class HashSet
extends AbstractSet
implements Set, Cloneable, Serializable

此类实现Set接口&#xff0c;由哈希表&#xff08;实际为HashMap实例&#xff09;支持。 对集合的迭代次序不作任何保证; 特别是&#xff0c;它不能保证订单在一段时间内保持不变。 这个类允许null元素。

这个类提供了基本操作&#xff08;add&#xff0c;remove&#xff0c;contains和size&#xff09;固定的时间性能&#xff0c;假定哈希函数将分散的桶中正确的元素。 迭代此集合需要与HashSet实例的大小&#xff08;元素数量&#xff09;和后台HashMap实例&#xff08;桶数&#xff09;的“容量”的总和成比例的时间。 因此&#xff0c;如果迭代性能很重要&#xff0c;不要将初始容量设置得太高&#xff08;或负载因子太低&#xff09;是非常重要的。

请注意&#xff0c;此实现不同步。 如果多个线程并发访问哈希集&#xff0c;并且至少有一个线程修改该集合&#xff0c;那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在&#xff0c;那么该集合应该使用Collections.synchronizedSet方法“包装”。 这最好在创建时完成&#xff0c;以防止对该集合的意外不同步访问&#xff1a;

Set s &#61; Collections.synchronizedSet(new HashSet(...));

该类iterator方法返回的迭代器是故障快速的 &#xff1a;如果集合在迭代器创建之后的任何时间被修改&#xff0c;除了通过迭代器自己的remove方法之外&#xff0c;迭代器会抛出一个ConcurrentModificationException 。 因此&#xff0c;面对并发修改&#xff0c;迭代器将快速而干净地失败&#xff0c;而不是在未来未确定的时间冒着任意的非确定性行为。

请注意&#xff0c;迭代器的故障快速行为无法保证&#xff0c;因为一般来说&#xff0c;在不同步并发修改的情况下&#xff0c;无法做出任何硬性保证。 失败快速迭代器尽力投入ConcurrentModificationException 。 因此&#xff0c;编写依赖于此异常的程序的正确性将是错误的&#xff1a;迭代器的故障快速行为应仅用于检测错误。

通过观察API文档我们得出结论&#xff1a;

  • 类允许为null
  • 不保证set的迭代顺序
  • 底层数据结构是哈希表&#xff08;元素是链表的数组&#xff09;
  • 哈希表依赖于哈希值存储
  • 添加功能底层依赖两个方法&#xff1a;

                   int hashCode()

                   boolean equals(Object obj)

存储自定义对象并遍历&#xff0c;参考代码&#xff1a;

创建Student2对象&#xff1a;

import java.util.Objects;public class Student2 {private String name;private int age;public Student2() {}public Student2(String name, int age) {this.name &#61; name;this.age &#61; age;}public String getName() {return name;}public void setName(String name) {this.name &#61; name;}public int getAge() {return age;}public void setAge(int age) {this.age &#61; age;}&#64;Overridepublic String toString() {return "Student2{" &#43;"name&#61;&#39;" &#43; name &#43; &#39;\&#39;&#39; &#43;", age&#61;" &#43; age &#43;&#39;}&#39;;}&#64;Overridepublic boolean equals(Object o) {if (this &#61;&#61; o) return true;if (o &#61;&#61; null || getClass() !&#61; o.getClass()) return false;Student2 student2 &#61; (Student2) o;return age &#61;&#61; student2.age &&Objects.equals(name, student2.name);}&#64;Overridepublic int hashCode() {return Objects.hash(name, age);}
}

创建HashSetDemo测试类&#xff1a;

/*存储自定义对象并遍历*/
import java.util.HashSet;public class HashSetDemo {public static void main(String[] args) {//创建集合对象HashSet hashSet &#61; new HashSet<>();//创建学生对象Student2 s1 &#61; new Student2("刘德华", 45);Student2 s2 &#61; new Student2("刘德华", 45);Student2 s3 &#61; new Student2("郭富城", 50);Student2 s4 &#61; new Student2("张学友", 60);//将学生对象添加到集合中hashSet.add(s1);hashSet.add(s2);hashSet.add(s3);hashSet.add(s4);//使用增强for遍历集合for (Student2 s : hashSet){System.out.println(s);}}
}

 输出结果&#xff1a;

Student2{name&#61;&#39;郭富城&#39;, age&#61;50}
Student2{name&#61;&#39;张学友&#39;, age&#61;60}
Student2{name&#61;&#39;刘德华&#39;, age&#61;45}




LinkedHashSet

查看API文档我们知道&#xff1a;

public class LinkedHashSet
extends HashSet
implements Set, Cloneable, Serializable

哈希表和链表实现了Set接口&#xff0c;具有可预测的迭代次序。 这种实现不同于HashSet&#xff0c;它维持于所有条目的运行双向链表。 该链表定义了迭代排序&#xff0c;它是将元素插入集合&#xff08;插入顺序 &#xff09; 的顺序 。 请注意&#xff0c;如果一个元件被重新插入到组插入顺序不受影响 。 &#xff08;元件e重新插入一组s如果当s.contains(e)将返回true之前立即调用s.add(e)被调用。&#xff09;

此实现可以让客户从提供的指定&#xff0c;通常杂乱无章的排序HashSet &#xff0c;而不会导致与其相关的成本增加TreeSet 。 它可以用于生成与原始文件具有相同顺序的集合的副本&#xff0c;而不管原始集的实现&#xff1a;

void foo(Set s) {Set copy &#61; new LinkedHashSet(s);...}

如果模块在输入上进行设置&#xff0c;复制它&#xff0c;并且稍后返回其顺序由该副本确定的结果&#xff0c;则此技术特别有用。 &#xff08;客户一般都喜欢以相同的顺序返回事情。&#xff09;

该类提供了所有可选的Set操作&#xff0c;并允许null元素。 像HashSet&#xff0c;它提供了基本操作&#xff08;add&#xff0c;contains和remove&#xff09;稳定的性能&#xff0c;假定散列函数散桶中适当的元件。 性能可能略低于HashSet &#xff0c;由于维护链表的额外费用&#xff0c;但有一个例外&#xff1a;LinkedHashSet的迭代需要与集合的大小成比例的时间&#xff0c;无论其容量如何。 HashSet的迭代可能更昂贵&#xff0c;需要与其容量成比例的时间。

链接哈希集具有影响其性能的两个参数&#xff1a; 初始容量负载因子 。 它们的定义精确到HashSet 。 但是请注意&#xff0c;该惩罚为初始容量选择非常高的值是该类比HashSet不太严重的&#xff0c;因为迭代次数对于这个类是由容量不受影响。

请注意&#xff0c;此实现不同步。 如果多个线程同时访问链接的散列集&#xff0c;并且至少有一个线程修改该集合&#xff0c;那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在&#xff0c;则应该使用Collections.synchronizedSet方法“包装”。 这最好在创建时完成&#xff0c;以防止对该集合的意外不同步访问&#xff1a;

Set s &#61; Collections.synchronizedSet(new LinkedHashSet(...));

该类iterator方法返回的迭代器是故障快速的 &#xff1a;如果在创建迭代器之后的任何时间对该集合进行了修改&#xff0c;除了通过迭代器自己的remove方法之外&#xff0c;迭代器将会抛出一个ConcurrentModificationException 。 因此&#xff0c;面对并发修改&#xff0c;迭代器将快速而干净地失败&#xff0c;而不是在未来未确定的时间冒着任意的非确定性行为。

请注意&#xff0c;迭代器的故障快速行为无法保证&#xff0c;因为一般来说&#xff0c;在不同步并发修改的情况下&#xff0c;无法做出任何硬性保证。 失败快速迭代器尽力投入ConcurrentModificationException 。 因此&#xff0c;编写依赖于此异常的程序的正确性将是错误的&#xff1a;迭代器的故障快速行为应仅用于检测错误。

这个班是Java Collections Framework的会员 。

通过观察API文档我们得出结论&#xff1a;

  • 元素有序唯一
  • 由链表保证元素有序
  • 由哈希表保证元素唯一

参考代码&#xff1a;

import java.util.LinkedHashSet;
import java.util.TreeSet;public class LinkedHashSetDemo {public static void main(String[] args) {//创建LinkedHashSet集合对象LinkedHashSet arr &#61; new LinkedHashSet<>();//创建TreeSet集合对象TreeSet strings &#61; new TreeSet<>();//添加元素到LinkedHashSet集合arr.add("hello");arr.add("world");arr.add("java");arr.add("bigdata");arr.add("hadoop");arr.add("hello");arr.add("hello");arr.add("java");arr.add("spark");arr.add("flink");arr.add("world");arr.add("hadoop");//添加元素到TreeSet集合strings.add("hello");strings.add("world");strings.add("java");strings.add("bigdata");strings.add("hadoop");strings.add("hello");strings.add("hello");strings.add("java");strings.add("spark");strings.add("flink");strings.add("world");strings.add("hadoop");System.out.println("遍历LinkedHashSet集合&#xff1a;");for (String s : arr){System.out.println(s);}System.out.println("&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;");System.out.println("遍历TreeSet集合&#xff1a;");for (String s1 : strings) {System.out.println(s1);}}}

输出结果&#xff1a;

遍历LinkedHashSet集合&#xff1a;
hello
world
java
bigdata
hadoop
spark
flink
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
遍历TreeSet集合&#xff1a;
bigdata
flink
hadoop
hello
java
spark
world

通过观察代码和输出结果我们得出结论:

  • LinkedHashSet底层数据结构是哈希表和双向链表
  • 哈希表保证了元素唯一
  • 链表保证了元素的有序&#xff08;存储和取出顺序一致&#xff09;

 通过输出结果我们发现&#xff0c;结果去重了&#xff0c;而且输出的结果顺序与添加的顺序不一样&#xff0c;这是为什么呢&#xff1f;

我们查看原码&#xff1a;

public interface Set extends Collection{}public class HashSet extends AbstractSet implements Set{private static final Object PRESENT &#61; new Object();private transient HashMap map;public boolean add(E e) { //E -- String //e -- "hello"return map.put(e, PRESENT)&#61;&#61;null;}
}public class HashMap extends AbstractMap implements Map{public V put(K key, V value) {//key -- "hello"//value -- new Object()return putVal(hash(key), key, value, false, true);}//简单理解为调用元素类的hashCode()方法计算哈希值static final int hash(Object key) { //"hello"int h;return (key &#61;&#61; null) ? 0 : (h &#61; key.hashCode()) ^ (h >>> 16);}final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {//理解为哈希表存储的是一个一个的结点数组Node[] tab; Node p; int n, i;//判断哈希表是否已经初始化完毕&#xff0c;如果没有初始化&#xff0c;就在这里初始化if ((tab &#61; table) &#61;&#61; null || (n &#61; tab.length) &#61;&#61; 0)n &#61; (tab &#61; resize()).length;//根据元素对象计算好的哈希值再进行一次与运算&#xff0c;计算出的是该元素存储在哈希表中的位置//如果该元素的位置是null,说明该位置没有元素&#xff0c;可以进行存储//就创建新的结点&#xff0c;存储元素//分析到这一步我们得出第一个结论&#xff0c;存储的位置与元素类中的hashCode()有关。if ((p &#61; tab[i &#61; (n - 1) & hash]) &#61;&#61; null)tab[i] &#61; newNode(hash, key, value, null);else {//如果该元素的位置不是null&#xff0c;说明这个位置上已经有元素了&#xff0c;可以确定是哈希值是一样的//但是呢&#xff0c;我们并不能确定两个值是不是一样的Node e; K k;//先将存入元素的哈希值与该位置中元素的哈希值做比较//如果哈希值都不一样&#xff0c;继续走判断instanceof()//如果哈希值都一样,会调用元素的equals(k)方法进行比较//如果equals(k)方法进行比较结果是false,继续向下执行&#xff0c;最终会将元素插入到集合中或者不插入//如果equals(k)方法进行比较结果是true,表示元素的哈希值和内容都一样&#xff0c;表示元素重复了//就覆盖&#xff0c;从现象上来看&#xff0c;其实就是不赋值//说到这里我们已经知道了add()方法和hashCode()以及equals()方法有关//会不会去重取决于元素类型有没有重写hashCode()以及equals()方法if (p.hash &#61;&#61; hash &&((k &#61; p.key) &#61;&#61; key || (key !&#61; null && key.equals(k))))e &#61; p;else if (p instanceof TreeNode)e &#61; ((TreeNode)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount &#61; 0; ; &#43;&#43;binCount) {if ((e &#61; p.next) &#61;&#61; null) {p.next &#61; newNode(hash, key, value, null);if (binCount >&#61; TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash &#61;&#61; hash &&((k &#61; e.key) &#61;&#61; key || (key !&#61; null && key.equals(k))))break;p &#61; e;}}if (e !&#61; null) { // existing mapping for keyV oldValue &#61; e.value;if (!onlyIfAbsent || oldValue &#61;&#61; null)e.value &#61; value;afterNodeAccess(e);return oldValue;}}&#43;&#43;modCount;if (&#43;&#43;size > threshold)resize();afterNodeInsertion(evict);return null;}}



TreeSet

通过查看API文档我们知道&#xff1a;

public class TreeSet
extends AbstractSet
implements NavigableSet, Cloneable, Serializable

A NavigableSet实现基于TreeMap 。 的元件使用其有序natural ordering &#xff0c;或由Comparator集合创建时提供&#xff0c;这取决于所使用的构造方法。

此实现提供了基本的操作&#xff08;保证的log&#xff08;n&#xff09;时间成本add &#xff0c; removecontains &#xff09;。

需要注意的是由一组&#xff08;无论是否提供了明确的比较器&#xff09;保持的顺序必须与equals一致 &#xff0c;如果它是要正确实现Set接口。 &#xff08;参见ComparableComparator一致的精确定义与equals&#xff09;。这是因为该Set接口在来定义equals的操作&#xff0c;但一个TreeSet例如使用其执行所有元件比较compareTo &#xff08;或compare &#xff09;方法&#xff0c;于是两个通过该方法认为相等的元素从集合的角度来看是相等的。 集合的行为明确定义的&#xff0c;即使其排序与equals不一致; 它只是没有遵守Set界面的总体合同。

请注意&#xff0c;此实现不同步。 如果多个线程并发访问树&#xff0c;并且至少有一个线程修改该集合&#xff0c;则必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在&#xff0c;那么该集合应该使用Collections.synchronizedSortedSet方法“包装”。 这最好在创建时完成&#xff0c;以防止对该集合的意外不同步访问&#xff1a;

SortedSet s &#61; Collections.synchronizedSortedSet(new TreeSet(...));

该类iterator方法返回的迭代器是故障快速的 &#xff1a;如果在迭代器创建之后的任何时间对该集合进行了修改&#xff0c;除了通过迭代器自己的remove方法之外&#xff0c;迭代器将抛出一个ConcurrentModificationException 。 因此&#xff0c;面对并发修改&#xff0c;迭代器将快速而干净地失败&#xff0c;而不是在未来未确定的时间冒着任意的非确定性行为。

请注意&#xff0c;迭代器的故障快速行为无法保证&#xff0c;因为一般来说&#xff0c;在不同步并发修改的情况下&#xff0c;无法做出任何硬性保证。 失败快速迭代器尽力投入ConcurrentModificationException 。 因此&#xff0c;编写依赖于此异常的程序的正确性将是错误的&#xff1a;迭代器的故障快速行为应仅用于检测错误。

这个班是Java Collections Framework的会员 。

通过观察API文档我们得出结论&#xff1a;

  • 使用元素的自然顺序对元素进行排序
  • 或者根据创建Set时提供的Comparator进行排序
  • 具体取决于使用的构造方法
  • 底层数据结构是红黑树&#xff08;红黑树是一种自平衡的二叉树&#xff09;

参考代码&#xff1a;

import java.util.TreeSet;public class TreeSetDemo1 {public static void main(String[] args) {//创建集合对象TreeSet ts &#61; new TreeSet<>();//添加元素到集合中ts.add(20);ts.add(18);ts.add(23);ts.add(24);ts.add(66);ts.add(12);ts.add(18);ts.add(20);ts.add(23);ts.add(2);//使用增强for循环遍历集合for (Integer t : ts) {System.out.println(t);}}
}

输出结果&#xff1a;

2
12
18
20
23
24
66

 通过观察代码和运行结果我们得出结论&#xff1a;TreeSet元素唯一&#xff0c;元素顺序可以按照某种规则进行排序&#xff1a;自然排序、比较器排序。

那去重又是为什么呢&#xff1f;我们查看原码&#xff1a;

public abstract class AbstractCollection implements Collection{}public abstract class AbstractSet extends AbstractCollection implements Set{}public class TreeSet extends AbstractSet implements NavigableSet, Cloneable, java.io.Serializable{private transient NavigableMap m;private static final Object PRESENT &#61; new Object();public TreeSet() {this(new TreeMap());}TreeSet(NavigableMap m) { //NavigableMap m &#61; new TreeMap()this.m &#61; m;}public boolean add(E e) {//E -- Integer//e -- 20return m.put(e, PRESENT)&#61;&#61;null;}
} public class TreeMap extends AbstractMap implements NavigableMap{private transient Entry root;public TreeMap() {comparator &#61; null;}public V put(K key, V value) {//key -- 18//value -- new Object()Entry t &#61; root;//判断根有没有元素&#xff0c;如果没有&#xff0c;为当前元素值创建一个结点&#xff0c;当作树的根结点if (t &#61;&#61; null) {compare(key, key); // type (and possibly null) checkroot &#61; new Entry<>(key, value, null);size &#61; 1;modCount&#43;&#43;;return null;}int cmp; //0Entry parent; //null// split comparator and comparable pathsComparator cpr &#61; comparator; // cpr &#61; null;if (cpr !&#61; null) {do {parent &#61; t;cmp &#61; cpr.compare(key, t.key);if (cmp <0)t &#61; t.left;else if (cmp > 0)t &#61; t.right;elsereturn t.setValue(value);} while (t !&#61; null);}else {if (key &#61;&#61; null)throw new NullPointerException();&#64;SuppressWarnings("unchecked")Comparable k &#61; (Comparable) key;do {parent &#61; t;cmp &#61; k.compareTo(t.key);if (cmp <0)t &#61; t.left;else if (cmp > 0)t &#61; t.right;elsereturn t.setValue(value);} while (t !&#61; null);}Entry e &#61; new Entry<>(key, value, parent);if (cmp <0)parent.left &#61; e;elseparent.right &#61; e;fixAfterInsertion(e);size&#43;&#43;;modCount&#43;&#43;;return null;}
}



TreeSet存储对学生对象遍历

参考代码1&#xff1a;

创建Student3类&#xff1a;

public class Student3 {private String name;private int age;//创建无参的构造方法public Student3() {}//创建有参的构造方法public Student3(String name, int age) {this.name &#61; name;this.age &#61; age;}//创建getXxx()和setXxx()方法public String getName() {return name;}public void setName(String name) {this.name &#61; name;}public int getAge() {return age;}public void setAge(int age) {this.age &#61; age;}//重写toString方法&#64;Overridepublic StringtoString() {return "Struden3{" &#43;"name&#61;&#39;" &#43; name &#43; &#39;\&#39;&#39; &#43;", age&#61;" &#43; age &#43;&#39;}&#39;;}}

创建TreeSeDemo2对象&#xff1a;

import java.util.TreeSet;public class TreeSetDemo2 {public static void main(String[] args) {//创建集合对象TreeSet ts &#61; new TreeSet<>();//创建学生集合对象Student3 s1 &#61; new Student3("张三", 24);Student3 s2 &#61; new Student3("李四", 32);Student3 s3 &#61; new Student3("王二", 53);Student3 s4 &#61; new Student3("麻子", 14);Student3 s5 &#61; new Student3("靓仔", 23);Student3 s6 &#61; new Student3("华仔", 67);Student3 s7 &#61; new Student3("老街", 35);//将学生添加到集合中ts.add(s1);ts.add(s2);ts.add(s3);ts.add(s4);ts.add(s5);ts.add(s6);ts.add(s7);//使用增强for循环遍历集合for (Object t : ts) {System.out.println(t);}}
}

输出结果&#xff1a;

 输出结果报错了&#xff0c;这是为什么呢&#xff1f;我们查看原码:

public abstract class AbstractCollection implements Collection{}public abstract class AbstractSet extends AbstractCollection implements Set{}public class TreeSet extends AbstractSet implements NavigableSet, Cloneable, java.io.Serializable{private transient NavigableMap m;private static final Object PRESENT &#61; new Object();public TreeSet() {this(new TreeMap());}TreeSet(NavigableMap m) { //NavigableMap m &#61; new TreeMap()this.m &#61; m;}public boolean add(E e) {//E -- Integer//e -- 20return m.put(e, PRESENT)&#61;&#61;null;}
} public class TreeMap extends AbstractMap implements NavigableMap{private transient Entry root;public TreeMap() {comparator &#61; null;}public V put(K key, V value) {//key -- 18//value -- new Object()Entry t &#61; root;//判断根有没有元素&#xff0c;如果没有&#xff0c;为当前元素值创建一个结点&#xff0c;当作树的根结点if (t &#61;&#61; null) {compare(key, key); // type (and possibly null) checkroot &#61; new Entry<>(key, value, null);size &#61; 1;modCount&#43;&#43;;return null;}int cmp; //0Entry parent; //null// split comparator and comparable pathsComparator cpr &#61; comparator; // cpr &#61; null;if (cpr !&#61; null) {do {parent &#61; t;cmp &#61; cpr.compare(key, t.key);if (cmp <0)t &#61; t.left;else if (cmp > 0)t &#61; t.right;elsereturn t.setValue(value);} while (t !&#61; null);}else {if (key &#61;&#61; null)throw new NullPointerException();&#64;SuppressWarnings("unchecked")Comparable k &#61; (Comparable) key;do {parent &#61; t;cmp &#61; k.compareTo(t.key);if (cmp <0)t &#61; t.left;else if (cmp > 0)t &#61; t.right;elsereturn t.setValue(value);} while (t !&#61; null);}Entry e &#61; new Entry<>(key, value, parent);if (cmp <0)parent.left &#61; e;elseparent.right &#61; e;fixAfterInsertion(e);size&#43;&#43;;modCount&#43;&#43;;return null;}
}

 通过查看原码我们得知&#xff1a;由于我们这里创建TreeSet对象调用 的是无参构造方法&#xff0c;所以走的是自然排序&#xff0c;而底层原码有一步向下转型&#xff1a;

Comparable k &#61; (Comparable) key;

原因是我们Student3类没有实现Comparable接口&#xff0c;无法向下转型&#xff0c;所以报错了。

那么实现一下Student3的Comparable接口&#xff1a;

 我们再运行一下&#xff1a;

return 0;

 return 1;

 return -1;

 通过不同的返回值我们发现&#xff1a;其实这里的返回值是根据我们的TreeSet的规则进行升序或降序的&#xff0c;比如我们想在去重的前提下&#xff0c;按照年龄进行排序&#xff1a;

returen this.age &#61; o.age;

 但题目的要求是年龄一样&#xff0c;姓名不一定不一样

 通过运行结果我们发现姓名一样&#xff0c;年龄一样&#xff0c;只保留一开始添加到集合中的学生对象。&#xff08;达到了题目的要求&#xff09;

完整代码如下&#xff1a;

Student3&#xff1a;

public class Student3 implements Comparable{private String name;private int age;//创建无参的构造方法public Student3() {}//创建有参的构造方法public Student3(String name, int age) {this.name &#61; name;this.age &#61; age;}//创建getXxx()和setXxx()方法public String getName() {return name;}public void setName(String name) {this.name &#61; name;}public int getAge() {return age;}public void setAge(int age) {this.age &#61; age;}//重写toString方法&#64;Overridepublic StringtoString() {return "Struden3{" &#43;"name&#61;&#39;" &#43; name &#43; &#39;\&#39;&#39; &#43;", age&#61;" &#43; age &#43;&#39;}&#39;;}&#64;Overridepublic int compareTo(Student3 o) {//返回第一个学生对象
// return 0;//升序
// return 1;//降序
// return -1;//年龄去重
// return this.age - o.age;//姓名年龄都去重int i &#61; this.age - o.age;int i2 &#61; i &#61;&#61; 0 ? this.name.compareTo(o.name) : i;return i2;}}

TreeSetDemo2&#xff1a;

import java.util.TreeSet;public class TreeSetDemo2 {public static void main(String[] args) {//创建集合对象TreeSet ts &#61; new TreeSet<>();//创建学生集合对象Student3 s1 &#61; new Student3("张三", 24);Student3 s2 &#61; new Student3("李四", 32);Student3 s3 &#61; new Student3("王二", 53);Student3 s4 &#61; new Student3("麻子", 14);Student3 s5 &#61; new Student3("靓仔", 23);Student3 s55 &#61; new Student3("靓仔", 23);Student3 s555 &#61; new Student3("靓仔", 18);Student3 s6 &#61; new Student3("华仔", 67);Student3 s7 &#61; new Student3("老街", 35);//将学生添加到集合中ts.add(s1);ts.add(s2);ts.add(s3);ts.add(s4);ts.add(s5);ts.add(s6);ts.add(s7);ts.add(s55);//使用增强for循环遍历集合for (Object t : ts) {System.out.println(t);}}
}

输出结果&#xff1a;

Struden3{name&#61;&#39;麻子&#39;, age&#61;14}
Struden3{name&#61;&#39;靓仔&#39;, age&#61;23}
Struden3{name&#61;&#39;张三&#39;, age&#61;24}
Struden3{name&#61;&#39;李四&#39;, age&#61;32}
Struden3{name&#61;&#39;老街&#39;, age&#61;35}
Struden3{name&#61;&#39;王二&#39;, age&#61;53}
Struden3{name&#61;&#39;华仔&#39;, age&#61;67}



&#x1f536;到底啦&#xff01;给靓仔一个关注吧&#xff01;&#x1f536;


推荐阅读
  • 如何高效启动大数据应用之旅?
    在前一篇文章中,我探讨了大数据的定义及其与数据挖掘的区别。本文将重点介绍如何高效启动大数据应用项目,涵盖关键步骤和最佳实践,帮助读者快速踏上大数据之旅。 ... [详细]
  • 投融资周报 | Circle 达成 4 亿美元融资协议,唯一艺术平台 A 轮融资超千万美元 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本文介绍了如何利用ObjectMapper实现JSON与JavaBean之间的高效转换。ObjectMapper是Jackson库的核心组件,能够便捷地将Java对象序列化为JSON格式,并支持从JSON、XML以及文件等多种数据源反序列化为Java对象。此外,还探讨了在实际应用中如何优化转换性能,以提升系统整体效率。 ... [详细]
  • 2012年9月12日优酷土豆校园招聘笔试题目解析与备考指南
    2012年9月12日,优酷土豆校园招聘笔试题目解析与备考指南。在选择题部分,有一道题目涉及中国人的血型分布情况,具体为A型30%、B型20%、O型40%、AB型10%。若需确保在随机选取的样本中,至少有一人为B型血的概率不低于90%,则需要选取的最少人数是多少?该问题不仅考察了概率统计的基本知识,还要求考生具备一定的逻辑推理能力。 ... [详细]
  • 第二章:Kafka基础入门与核心概念解析
    本章节主要介绍了Kafka的基本概念及其核心特性。Kafka是一种分布式消息发布和订阅系统,以其卓越的性能和高吞吐量而著称。最初,Kafka被设计用于LinkedIn的活动流和运营数据处理,旨在高效地管理和传输大规模的数据流。这些数据主要包括用户活动记录、系统日志和其他实时信息。通过深入解析Kafka的设计原理和应用场景,读者将能够更好地理解其在现代大数据架构中的重要地位。 ... [详细]
  • 揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节
    揭秘腾讯云CynosDB计算层设计优化背后的不为人知的故事与技术细节 ... [详细]
  • 在HDU 1166敌军布阵问题中,通过运用线段树数据结构,可以高效地计算指定区间的敌军数量。该算法不仅能够在限定的时间和内存条件下快速求解,还能够灵活应对动态变化的战场局势,为实时决策提供支持。 ... [详细]
  • 在第二课中,我们将深入探讨Scala的面向对象编程核心概念及其在Spark源码中的应用。首先,通过详细的实战案例,全面解析Scala中的类和对象。作为一门纯面向对象的语言,Scala的类设计和对象使用是理解其面向对象特性的关键。此外,我们还将介绍如何通过阅读Spark源码来进一步巩固对这些概念的理解。这不仅有助于提升编程技能,还能为后续的高级应用开发打下坚实的基础。 ... [详细]
  • 单链表的高效遍历及性能优化策略
    本文探讨了单链表的高效遍历方法及其性能优化策略。在单链表的数据结构中,插入操作的时间复杂度为O(n),而遍历操作的时间复杂度为O(n^2)。通过在 `LinkList.h` 和 `main.cpp` 文件中对单链表进行封装,我们实现了创建和销毁功能的优化,提高了单链表的使用效率。此外,文章还介绍了几种常见的优化技术,如缓存节点指针和批量处理,以进一步提升遍历性能。 ... [详细]
  • 本文探讨了 Kafka 集群的高效部署与优化策略。首先介绍了 Kafka 的下载与安装步骤,包括从官方网站获取最新版本的压缩包并进行解压。随后详细讨论了集群配置的最佳实践,涵盖节点选择、网络优化和性能调优等方面,旨在提升系统的稳定性和处理能力。此外,还提供了常见的故障排查方法和监控方案,帮助运维人员更好地管理和维护 Kafka 集群。 ... [详细]
  • 美团优选推荐系统架构师 L7/L8:算法与工程深度融合 ... [详细]
  • 深入解析 Python 中的 NumPy 加法函数 numpy.add() ... [详细]
  • HBase Java API 进阶:过滤器详解与应用实例
    本文详细探讨了HBase 1.2.6版本中Java API的高级应用,重点介绍了过滤器的使用方法和实际案例。首先,文章对几种常见的HBase过滤器进行了概述,包括列前缀过滤器(ColumnPrefixFilter)和时间戳过滤器(TimestampsFilter)。此外,还详细讲解了分页过滤器(PageFilter)的实现原理及其在大数据查询中的应用场景。通过具体的代码示例,读者可以更好地理解和掌握这些过滤器的使用技巧,从而提高数据处理的效率和灵活性。 ... [详细]
  • 如何在Spark数据排序过程中有效避免内存溢出(OOM)问题
    本文深入探讨了在使用Spark进行数据排序时如何有效预防内存溢出(OOM)问题。通过具体的代码示例,详细阐述了优化策略和技术手段,为读者在实际工作中遇到类似问题提供了宝贵的参考和指导。 ... [详细]
author-avatar
GUOQIFENG_534
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有