1、Set
Collection有两大体系:
* A:List* 元素有序(指的是存储顺序和取出顺序是否一致),可重复
* B:Set
* 元素无序,唯一,不可重复
*
* 通过查看api我们发现,set和Collection功能一致
* 又因为它是接口,所以,找一个实现类HashSet
代码实例可知Set无序且唯一:
注意:1)set遍历方式两种,迭代器和增强for,普通for不可以,因为无序
2)Iterator迭代器也是泛型类,限制类型后不用,执行it.next()的时候就不用转型
3) set保证唯一行,在添加的过程中会自己判断,如果重复则不会添加。
2、HashSet
HashSet存储自定义对象,实现了set接口。
既然set能保证唯一性,那么自定义对象呢,我们认为一个对象,如果成员变量值都相同,则同为一个对象。
* 请思考:
* 为什么字符串可以保证唯一性,而Student对象不能保证唯一性。
* A:哪里出问题了
* 经过简单分析,我们知道了在add方法出问题了。
* B:怎么解决
* 看源码
* if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
{
???唯一
}
上面的判断跟对象的hashCode()方法有关
在If判断语句中,如果e.hash == hash是false那么不会执行右边,就不会走到equals(k)方法。
因为对象是new出来的,地址值不一样,hashcode是根据地址值算出来了,故hashcode也不一样。
即左边永远是false,我们要做的事情就是保证左边是true
分析:
A:这个判断跟hashcode有关
B:这个判断跟equals有关
若想要走equals就保证左边是true,若要保证左边是true就要重写hashcode方法
* HashSet底层数据结构,哈希表。
* 哈希表根据哈希算法存储的,存储的就是对象的哈希值。
* 由于不同对象的哈希值不一样,所以6个元素全部存储成功,所以没有去掉重复值。
* 而我们向去掉重复值,则么办呢?
* 首先我们重写了hashCode(),返回值是0,保证了所有的对象的哈希值相同
* 接着重写了equals()方法,比较对象的成员变量。
* 重写后,这个时候的存储,哈希表提供了哈希桶结构,这个结构是处理哈希值相同的情况。
* 哈希桶结构用于处理哈希值相同的情况。
* 当哈希值相同的情况系,对象会存储在一个桶结构中。
* 但是,看源码后,我们知道它会走equals()方法。看成员变量值是否相同。
*
* 问题现在是解决了,但是效率又出问题了因为每一个都会调用equal方法,我们需要改进代码,让效率高一些。
* 如何改变呢?
* 在hashCode()上想办法,让不可能相同的返回不同的哈希值,这样就不会走equals方法了。
* 怎么实现?
* 一般来说,我们在hashCode()方法上不会直接返回一个固定的值,这个值应该是变化的。
* 这个方法它本质是跟对象的成员变量相关。我们只需要把对象的成员变量值相加即可。
* hashCode();把所有的成员变量值相加,如果是引用类型,用哈希值,如果是基本类型直接用值。
* Person:
* String name;
* int age;
* char sex;
* float score;
*
* public int hashCode()
* {
* return this.name.hashCode()+this.age +this.sex+this.score
* }
代码实现如下,自动生成:
3、TreeSet
TreeSet可以对元素进行排序,同时保证元素唯一。
使用元素的自然顺序对元素进行排序,或者根据创建set时提供的Comparator进行排序,具体取决于使用的构造方法。
1)当对象是String时会按自然顺序对字符串排序:
输出结果每次都是固定的:abcde
hello
java
world
zon
由上面结果可知,输出结果有序、唯一、且不重复,这个顺序是自然的顺序首字母有a到z,
原因是使用了new TreeSet<String>( )这种构造方法。
2)当TreeSet是Student对象时怎么排序:
报错:Exception in thread "main" java.lang.ClassCastException:
* cn.itcast_02.Student cannot be cast to java.lang.Comparable
* Student不能转成Comparable;
分析:
Interface Comparable<T>此接口强行对实现它的每个类的对象进行排序。
这种排序称为类的自然排序,类的compareTo方法被称为它的自然比较方法。如果使用TreeSet<Student> ts = new TreeSet<Student>();这种无参构造方法,会对对象进行自然排序。
这时需要对象所属的类去实现Comparable接口,因为String类实现了Comparable接口,所以可以排序。
Student类没有实现接口,不知道怎么排序,所以需要Student类去实现Comparable接口,告诉它怎么对对象排序。
* TerrSet保证元素排序有两种方式:
* A:自然排序,让对象所属的类去实现Comparable接口,使用的是无参构造。
即类要实现Comparable接口,并重写compareTo()方法,TreeSet对象调用add()方法时,会将存入的对象提升为Comparable类型,然后调用对象中的compareTo()方法进行比较,根据比较的返回值进行存储。 正如上面程序,Student类没有实现接口,所以不能提升为Comparable类型,就会报错。
因为TreeSet底层是二叉树,当compareTo方法返回0时,不存储;当compareTo方法返回正数时,存入二叉树的右子树;当compareTo方法返回负数时,存入二叉树的左子树。如果一个类没有实现Comparable接口就将该类对象存入TreeSet集合,会发生类型转换异常。
* TreeSet:底层数据结构是二叉树结构
* 那么二叉树是如何存储数据的呢?
* 规则:
* 第一个添加的数据作为根节点。
* 从第二个开始
* 每一个数据从根节点开始比较
* 如果大了往又放。
* 如果小了往左放
* 如果相同,替换
*
* 二叉树的遍历方式有三种
* 从根节点开始获取数据的规则是按照数据的,左,中,右原则
* B:比较器接口Comparator,带参构造。
比较器接口的实现方法:
1》首先需要按年龄大小排
如果需要按姓名长短排呢?
重新写一个MyComparator2 implements Comparator<Student>,然后在主函数中:
TreeSet<Student> ts = new TreeSet<Student>(new MyComparator2());
另外也可以采用匿名内部类实现:
这两种方式的比较:
* TerrSet保证元素排序有两种方式:
* A:自然排序,让对象所属的类去实现Comparable接口,使用的是无参构造。
* B:比较器接口Comparator,带参构造。
*
* 还是用第二种方法比较好,比如MyComparator()是按年龄排的,MyComparator2()是按姓名长度排的
* 到时候想让程序按什么排就可以通过
* TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
* 或者TreeSet<Student> ts = new TreeSet<Student>(new MyComparator2());
*实现
* 开发原则:
* 对修改关闭,对扩展开放
4、Collections
Collection和Collections的区别?
* Collection:是Collection集合的顶层接口,定义了Collection集合的共性方法。* Collections:是一个类,定义了针对Collection集合操作的功能,有排序,查找,反转等。
*
* Collections的功能:
* 排序:static void sort(List<T> list)
* 二分查找:static <T> int binarySearch(List list, T key)
* 返回的是索引
* 反转:static void reverse(List<?> list)
* 最大值:max(Collection coll)
* 随机置换:shuffle(List<?> list)