Set实现类
<3> 哈希表浅说
我们从逻辑上最简单的理解这种存储结构
HashSet是通过链表加数组实现的。
那么我们给出16个位置,索引为 0-15。每次通过计算得出的哈希值是比较大的,我们不可能让哈希值作为索引下标。于是采用了取模也就是对16取余数,余数是多少就会存储到那个位置。不过在存储之前也就会进行判断,判断方法已经说明,不再赘述。如果存储到数组的同一个位置,后面就会采用在该位置进行链式存储。如上图。
HashSet的默认构造方法HashSet的默认初始容量为16
构造方法摘要
HashSet()
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75
2:实现类LinkedHashSet
完整类定义
public class LinkedHashSet<E>extends HashSet<E>implements Set<E>, Cloneable, Serializable
通过类定义我们可以发现,LinkedHashSet是HashSet的继承类。并且实现了Set类。
LinkedHashSet是通过双向链表实现的。我们知道HashSet存取元素是无序的,LinkedHashSet采用双向链表,双向链表在逻辑存储上是连续的,所以LinkedHashSet是有序存储的。
JDKAPI 也有详细的说明
我们来看部分说明
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序不 受在 set 中重新插入的 元素的影响。(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到 set s 中。)
<1>方法说明
JDK API给出了具体的继承的方法
从类 java.util.HashSet 继承的方法
add, clear, clone, contains, isEmpty, iterator, remove, size
从类 java.util.AbstractSet 继承的方法
equals, hashCode, removeAll
从类 java.util.AbstractCollection 继承的方法
addAll, containsAll, retainAll, toArray, toArray, toString
从类 java.lang.Object 继承的方法
finalize, getClass, notify, notifyAll, wait, wait, wait
从接口 java.util.Set 继承的方法
add, addAll, clear, contains, containsAll, equals, hashCode, isEmpty, iterator, remove, removeAll, retainAll, size, toArray, toArray
验证元素输出是否有序和唯一
package java_practice;
import java.util.LinkedHashSet;
public class LinkedHashSet_Demo {
public static void main(String args[]){
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
lhs.add("Hello");
lhs.add("world");
lhs.add("Hello");
lhs.add("every body");
System.out.println(lhs);
}
}
注:常用的方法,已经说过了。比较深入的,会在后面用到的时候说,比如finalize()还是有很大的学问的。
3:实现类TreeSet
在类的定义中尽管没有点出实现Set集合,但是直接溯源还是可以认为其是Set集合的一种
我们来看完整的类定义
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
//从AbstractSet溯源
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {
所以我们也可以认为TreeSet实现了Set集合
JDK API 给出了TreeSet的部分说明
基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
当然还有其它的内容,但是文字凝聚太强,难以理解。
底层数据结构实现是红黑树,所以TreeSet存储的数据是有顺序的。
TreeSet集合的元素有序,按照一定的规则进行排序,具体的排序方式取决于采用的构造器,并且数还是不允许重复的。
<1>方法说明
<1>默认的自然排序方式(默认调用无参构造器)
举例直接上代码
TreeSet<Integer> in = new TreeSet<>();
in.add(10);
in.add(20);
in.add(30);
in.add(5);
in.add(4);
//遍历集合
for(Integer i :in)
{
System.out.println(i);
}
<2>自然排序Comparable的使用
现在我要采用TreeSet对我设计的一个学生类进行一个自然排序,看看是否可以成功?
package java_practice;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String args[])
{
TreeSet<Student> ts = new TreeSet<Student>();
Student s1 = new Student(29, "西施");
Student s2 = new Student(28, "王昭君");
Student s3 = new Student(30, "貂蝉");
Student s4 = new Student(33, "杨玉环");
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
for(Student s : ts)
{
System.out.println(s.getName()+""+s.getAge());
}
}
}
写代码感觉没问题,但是运行就出问题了
摘录出来
Exception in thread "main" java.lang.ClassCastException: java_practice.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
at java_practice.TreeSetDemo.main(TreeSetDemo.java:13)
关键的一句
Exception in thread "main" java.lang.ClassCastException: java_practice.Student cannot be cast to java.lang.Comparable
说明了学生类是不能够转为Comparable这个接口的。为什么不可以?
我们再去查看Comparable这个接口的说明
public interface Comparable<T>此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
那么我们的Student类需要去实现它
//Student 类
package java_practice;
public class Student implements Comparable<Student>{
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public int compareTo(Student s) {
int num = this.age -s.age;
return num;
//return 0;
}
}
//测试类
package java_practice;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String args[])
{
TreeSet<Student> ts = new TreeSet<Student>();
Student s1 = new Student(29, "西施");
Student s2 = new Student(28, "王昭君");
Student s3 = new Student(30, "貂蝉");
Student s4 = new Student(33, "杨玉环");
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
for(Student s : ts)
{
System.out.println(s.getName()+""+s.getAge());
}
}
}
public int compareTo(Student s) {
int num = this.age -s.age;
return num;
//return 0;
}
返回值,this放在前面就是升序排列,放在后面就是降序。如果你直接返回数字1的话,那么就顺序输出了。返回0的话,就只能输出一个元素。可以自己进行测试。
但是还有一个问题就是如果年龄相同的话,那么如果还要向集合中添加元素的话,那么是不会输出输出与前面对象年龄数据相同的后续对象的,那么我们可以添加条件。
具体还是操作CompareTo方法,稍微改一下。
public int compareTo(Student s) {
int num = this.age -s.age;
int num2 = num == 0?this.name.compareTo(s.name):num;
return num2;
}
这样就可以了。
那么你可能会有疑惑,为什么上面的Integer类型可以实现比较呢?因为Integer本身就实现了CompareTo方法,另外字符串也实现了这个方法。
- 点赞
- 收藏
- 关注作者
评论(0)