- Set 集合
- 常用子类:
TreeSet:有序存放
LinkedHashSet:使用链表
- HashSet
1,不能保证元素的排列顺序,散列存储:不记录添加顺序,排列顺序时,顺序有可能发生变化
2,HashSet 不是线程安全的,多个线程访问一个HashSet要使用同步代码
3,集合元素可以使 null,但是最多只能有一个
4,当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
5,Hash算法价值体现在速度,可以保证查询快速执行。当从HashSet中访问元素时,HashSet先计算该元素的hashCode(也就是该对象的hashCode方法返回值),然后直接到该HashCode对应的位置取出该元素; 在这里对象的hashCode就好比是数组里的索引,但是不是索引。
6,如果需要某个类的对象保存到HashSet集合中,覆写该类的equals()和hashCode()方法,应该尽量保证两个对象通过equals比较返回true时,他们的hashCode返回也相等。
说到这里,就不得不提object类中的hashCode() 方法:
HashSet 集合判断两个元素相等的标准:两个对象通过 equals() 方法比较相等,并且两个对象的 hashCode() 方法返回值也相等。如果两个对象通过 equals() 方法返回 true,这两个对象的 hashCode 值也应该相同。重写 hashCode() 方法的基本原则:1:在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值
2:当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等
3:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值
关于hashset值得注意的是:
package tz.web.main;import java.util.Iterator;import java.util.Set;import com.google.common.collect.Sets;public class Linkin{ private String name; public Linkin(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public static void main(String[] args) { Linkin linkin1 = new Linkin("huhu1"); Linkin linkin2 = new Linkin("huhu2"); Setset = Sets.newHashSet(linkin1,linkin2); System.out.println(set.size());//2 linkin1.setName("LinkinPark"); //这里的hashcode值变了,在删除的时候按照这个linkin1的hashcode值去找对象,结果就根本找不见了,所以没有删掉 set.remove(linkin1); System.out.println(set.size());//2 Iterator it = set.iterator(); while(it.hasNext()) { it.next(); //从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。 it.remove(); } System.out.println(set.size());//1 } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Linkin other = (Linkin) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
- LinkedHashSet
import java.util.LinkedHashSet;import java.util.Set;/** * * @version 1L * @author LinkinPark * @since 2014-11-10 * @motto 梦似烟花心似水,同学少年不言情 * @desc ^遍历LinkedHashSet集合里面的元素时,LinkedHashSet会按元素的添加顺序来访问集合里面的元素 */public class Linkin { public static void main(String[] args) { Setnames = new LinkedHashSet (); names.add("Binger"); names.add("LinkinPark"); //Binger LinkinPark for (String string : names) { System.out.println(string); } } }
- TreeSet
如果试图把一个对象添加到TreeSet时,该对象必须实现Comparable接口,否则程序会抛出异常。
使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,参与排序的元素必须是同一类型的,不然会发生ClassCastException异常,
TreeSet是SortedSet接口唯一的实现,与HashSet相比额外的方法有: Comparator comparator() 返回当前Set使用的Comparator 若返回null,表示以自然顺序排序。 Object first() 返回此 set 中当前第一个(最低)元素。 Object last() 返回此 set 中当前最后一个(最高)元素。 SortedSet subSet(Object fromElement, E toElement) 返回此 set 的部子集,其元素从 fromElement(包括)到 toElement(不包括)。 SortedSet headSet(Object toElement) 返回此 set 的部分子集,其元素严格小于 toElement。SortedSet tailSet(Object fromElement) 返回此 set 的部分子集,其元素大于等于 fromElement。
TreeSet 支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。
- 自然排序
对于TreeSet集合而言,判断两个对象相等的标准是:compareTo()方法比较返回 0。
- Comparable 的典型实现:
Character:按字符的 UNICODE 值来进行比较
Boolean:true 对应的包装类实例大于 false 对应的包装类实例
String:按字符串中字符的 UNICODE 值进行比较
Date、Time:后边的时间、日期比前面的时间、日期大
因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类的对象。当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过 equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回 0。
import java.util.Set;import java.util.TreeSet;/** * * @version 1L * @author LinkinPark * @since 2014-11-10 * @motto 梦似烟花心似水,同学少年不言情 * @desc ^如果试图把一个对象添加到TreeSet时,该对象必须实现Comparable接口,否则程序会抛出异常。 */public class Linkin implements Comparable{ private String name;//名字 private Integer age;//年龄 public Linkin(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public int compareTo(Object obj) { Linkin s = (Linkin)obj; return this.age - s.age; } public static void main(String[] args) { Linkin linkin1 = new Linkin("LinkinPark", 25); Linkin linkin2 = new Linkin("Binger", 24); Setlinkins = new TreeSet (); linkins.add(linkin1); linkins.add(linkin2); for (Linkin linkin : linkins) { //Binger LinkinPark System.out.println(linkin.getName()); } } }
- 定制排序
TreeSet的自然排序是根据元素的大小进行升序排序的,若想自己定制排序,比如降序排序,就可以使用Comparator接口了。该接口包含int compare(Object o1,Object o2)方法,用于比较两个对象的大小,比较结果和compareTo方法一致。要实现定制排序,需要在创建TreeSet集合对象时,提供一个一个Comparator对象,该对象里负责集合元素的排序逻辑。如果需要实现定制排序,则需要在创建 TreeSet 集合对象时,提供一个 Comparator 接口的实现类对象。由该 Comparator 对象负责集合元素的排序逻辑。
import java.util.Comparator;import java.util.Set;import java.util.TreeSet;/** * * @version 1L * @author LinkinPark * @since 2014-11-10 * @motto 梦似烟花心似水,同学少年不言情 * @desc ^ */public class Linkin { private String name;//名字 private Integer age;//年龄 public Linkin(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public static void main(String[] args) { Linkin linkin1 = new Linkin("LinkinPark", 25); Linkin linkin2 = new Linkin("Binger", 24); Setlinkins = new TreeSet ( new Comparator () { @Override public int compare(Linkin o1, Linkin o2) { Linkin obj1 = (Linkin) o1; Linkin obj2 = (Linkin) o2; return obj1.getAge() - obj2.getAge(); } }); linkins.add(linkin1); linkins.add(linkin2); for (Linkin linkin : linkins) { //Binger LinkinPark System.out.println(linkin.getName()); } } }
各Set实现类的性能分析: HashSet和TreeSet是Set的2个典型实现,到底如何选择HashSet和TreeSet呢?HashSet的性能总是比TreeSet好,特别是在最常用的添加,查询元素等操作,因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。LinkedHashSet是HashSet的一个子类,对于普通的插入,删除操作,LinkedHashSet总是比HashS慢一点,这是由维护链表所带来的额外开销造成的。不过真是因为有了链表,遍历LinkedHashSet会更快。实际开发中HashSet使用的是比较多的,其他的基本用不到。