}<span style="font-family: Simsun; background-color: rgb(255, 255, 255);">------- </span><a target=_blank href="http://www.itheima.com/" target="blank" style="font-family: Simsun; background-color: rgb(255, 255, 255);">android培训</a><span style="font-family: Simsun; background-color: rgb(255, 255, 255);">、</span><a target=_blank href="http://www.itheima.com/" target="blank" style="font-family: Simsun; background-color: rgb(255, 255, 255);">java培训</a><span style="font-family: Simsun; background-color: rgb(255, 255, 255);">、期待与您交流! ----------</span>
今天的学习内容是集合的Set结构
相比于Link结构,Set的特点是无序(存储和读取的顺序是不一致的),无索引,不可以重复存储相同元素。
Ste集合的这个帮派中,有HashSet和TreeSet两大马仔。两个各司其职,各有擅长的地方。
类 hashSet<.E>
这个类的特点是:
不保证迭代顺序;
也不保证顺序不变(上次输出结果和下次输出结果未必一样,即使没有对容器进行任何操作和修改);
但是能够保证元素不重复。(这恐怕就是hashSet的看家本领了)
hashSet这个唯一赖以生存的本领的实现原理是什么呢?
首先就是写在的名字上的hash。
1.HashSet原理
*我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率,降低了使用equals()方法的次数
*当HashSet调用add()方法存储对象的时候, 先调用对象的hashCode()方法得到一个哈希值, 然后在集合中查找是否有哈希值相同的对象
*如果没有哈希值相同的对象就直接存入集合
*如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行equals()比较,比较结果为false就存入, true则不存
2.将自定义类的对象存入HashSet去重复
类中必须重写hashCode()和equals()方法
hashCode(): 属性相同的对象返回值必须相同, 属性不同的返回值尽量不同(提高效率)
equals(): 属性相同返回true, 属性不同返
在向集合中添加元素时,内在的逻辑是先比较元素的hashCode,当hashCode一样时,再用equals方法比较。
Hashcode是方法hashCode()生成的很有可能是元素独有的标识。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result
+ ((name == null) ? 0 : name.hashCode());
return result;
}
PS:这里的prime的值为何是31?一方面是因为它是个不大不小的指数,这样算出来的hashCode更容易独一无二。另一方面,31是2^5-1,系统得到这个数比较快。
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
Tips:eclipse中,在自定义类中,shift + Alt + s 再按h ,可以自动生成hashCode和equals方法
LinkedHashSet
特点: 可以保证怎么存就怎么取
在操作上,LinkedHashSet没什么特殊之处。和其他集合的操作相同。
TreeSet
首先,TreeSet是一个用来对元素排序的的集合。这个功能很强大,以后就不用自己写那麻烦的双循环嵌套的排序算法了。
自定义类必须实现Comparable接口,才能成功被添加进TreeSet集合中。
实现Comparable接口就要重写CompareTo方法。当对象被添加进集合中时,集合会调用该对象的CompareTo方法和集合内的元素进行比较。
CompateTo 会返回一个int类型。
TreeSet的数据结构是二叉树。每个节点都有两个引用,分别指向下一级的左节点、右节点。
CompareTo的返回值是负数时,新元素存数在左边;是正数时,存放在右边;等于0时不存储。
读取的顺序是左中右。这样就实现了对元素的排序。
当然,自定义类的CompareTo你也可以重写,按实际要求做出修改。例如当要求允许重复元素时,就要把返回值0的情况归到不为零的判断中。
TreeSet的比较器排序。
比较器需要自己定义。因为只是在实例化TreeSet时才调用一次比较器,所以可以用匿名内部类的方式来定义。
例如:
<p>TreeSet<String> ts = newTreeSet<>(new Comparator<String>() { //定义比较器(new<span style="font-family: Arial, Helvetica, sans-serif;">Comparator(){}是Comparator的子类对象)</span></p><p><span style="white-space: pre;"></span>@Override</p><p> publicint compare(String s1, String s2) { //重写compare方法</p><p> intnum = s1.compareTo(s2); //比较内容</p><p> returnnum == 0 ? 1 : num; //如果内容一样返回一个不为0的数字即可</p><p> }</p><p>});</p>
两种排序方式的总结:
*a.自然顺序(Comparable)
*TreeSet类的add()方法中会把存入的对象提升为Comparable类型
*调用对象的compareTo()方法和集合中的对象比较
*根据compareTo()方法返回的结果进行存储
*b.比较器顺序(Comparator)
*创建TreeSet的时候可以制定 一个Comparator
*如果传入了Comparator的子类对象, 那么TreeSet就会按照比较器中的顺序排序
*add()方法内部会自动调用Comparator接口中compare()方法排序
*调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
*c.两种方式的区别
*TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)
*TreeSet如果传入Comparator,就优先按照Comparator