Set接口(HashSet,TreeSet和LinkedHashSet)
Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不 会接受这两个对象。
事实1:set就是就是相当于Map的kay。
事实2:hashSet是为快速查找而设计的set,存入hashSet的元素必须定义hashCode。而treeSet是保持次序的set,使用treeSet可以从set中提取有序的序列,元素必须实现conparable接口。而
linkedHashSet具有hashSet的查询速度,且内部是链表结构,因此遍历linkedHashSet时,结果会按照插入顺序显示!
HashSet
HashSet有以下特点
不能保证元素的排列顺序,顺序有可能发生变化
不是同步的
集合元素可以是null,但只能放入一个null
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
demo1:
用户自定义类型放进set容器里面,必须要重写equals和hashCode方法,否则会被认为是两个不同的元素。
import java.util.HashSet;
import java.util.Set;
public class overwriteHahcodeAndEquals {
public static void main(String[] args) {
Set<Object> set = new HashSet<Object>();
set.add(new Student("lopez",23));
set.add(new Student("lopez",23));
set.add(new Student("shengliang",33));
set.add(new Student("weikun",23));
set.add(new Student("chencheng",28));
System.out.println(set);
//output:[set.Student@1420a8f4, set.Student@9b725c34, set.Student@c2ce75cf, set.Student@8d6b816e]
//一共4个对象
}
}
class Student{//内部类
String name;
int age;
public Student(String name , int age){
this.name = name;
this.age = age;
}
public int hashCode(){//重写hsahCode方法
return age*name.hashCode();
}
public boolean equals(Object o){//重写equals方法
Student s = (Student)o;
return s.age == this.age && s.name.equals(this.name);
}
}
1.分别在hashCode和equals打断点debug,发现
在往Set中插入新的对象时,首先会用该对象的hashCode()与已经存在对象的hashCode()做比较,如果相等,那就不能插入,如果不等,才会调用equals()方法,如果equals结果为true,说明已经存在, 就不能再插入,如果为false,可以插入。
set.add(new String("asd"));
set.add(new String("asd"));
//set只得到一个asd,因为内容相同的String,即使不重写hashCode方法,得到的hashCode都是相同的。
package set;
import java.util.HashSet;
import java.util.Set;
public class overwriteHahcodeAndEquals {
public static void main(String[] args) {
Set<Object> set = new HashSet<Object>();
set.add(new Student("lopez",23));
set.add(new Student("lopez",23));
set.add(new Student("shengliang",33));
set.add(new Student("weikun",23));
set.add(new Student("chencheng",28));
System.out.println(set);
//output:[set.Student@c2ea3f, set.Student@1d5550d, set.Student@c21495, set.Student@1186fab, set.Student@14b7453]
//一共5个对象
}
}
class Student{//内部类
String name;
int age;
public Student(String name , int age){
this.name = name;
this.age = age;
}
}
针对demo2,做了个小测试:
package set;
import java.util.HashSet;
import java.util.Set;
public class overwriteHahcodeAndEquals {
public static void main(String[] args) {
Set<Object> set = new HashSet<Object>();
Student stu = new Student("lopez",23);
Student stu1 = new Student("lopez",23);
System.out.println("stu的hashCode值为:" + stu.hashCode());
System.out.println("stu1的hashCode值为:" + stu1.hashCode());
System.out.println("stu和stu1 equals比较" + stu.equals(stu1));
set.add(stu);
set.add(stu1);
set.add(new Student("shengliang",33));
set.add(new Student("weikun",23));
set.add(new Student("chencheng",28));
System.out.println(set);
}
}
class Student{//内部类
String name;
int age;
public Student(String name , int age){
this.name = name;
this.age = age;
}
/*public int hashCode(){
return age*name.hashCode();
}
public boolean equals(Object o){
Student s = (Student)o;
return s.age == this.age && s.name.equals(this.name);//int类型的比较和String类型的比较
}*/
}
由以下输出可得知:
LinkedHashSet
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好(链表结构),但是插入时性能稍微逊色于HashSet,因为插入时linkedHashSet需要维护链表结构。
package set;
import java.util.HashSet;
import java.util.LinkedHashSet;
public class CompareHashSetAndLinkedHashSet {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet set = new LinkedHashSet();
set.add("lopez1");
set.add("lopez2");
set.add("lopez3");
set.add("lopez4");
for (Object obj : set) {
System.out.println(obj.toString());
}
//output:
//lopez1
//lopez2
//lopez3
//lopez4
HashSet set1 = new HashSet();
set1.add("lopez1");
set1.add("lopez2");
set1.add("lopez3");
set1.add("lopez4");
for (Object obj : set1) {
System.out.println(obj.toString());
}
//lopez1
//lopez4
//lopez3
//lopez2
}
}
TreeSet类
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法
按自然顺序demo1:
package set;
import java.util.TreeSet;
import java.util.Iterator;
public class TestTreeSet
{
public static void main(String[] args)
{
TreeSet tree = new TreeSet();
tree.add("China");
tree.add("America");
tree.add("England");
tree.add("Japan");
tree.add("Chinese");
tree.add("lapez");
tree.add("lopez");
tree.add("lopez1");
Iterator iter = tree.iterator();
while(iter.hasNext())
{
System.out.println(iter.next());
}
}
//output: America
//China
//Chinese
//England
//Japan
//lapez
//lopez
//lopez1
}
定制排序
package set;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class TestTreeSet {
/** *//**
* @param args
*/
public static void main(String[] args) {
MyComparator<String> myComparator = new MyComparator<String>();
// /不添加自定义排序
TreeSet<String> treeSet1 = new TreeSet<String>();
treeSet1.add("c");
treeSet1.add("a");
treeSet1.add("b");
Iterator<String> iterator1 = treeSet1.iterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
// /添加自定义排序
TreeSet<String> treeSet2 = new TreeSet<String>(myComparator);
treeSet2.add("c");
treeSet2.add("a");
treeSet2.add("b");
Iterator<String> iterator2 = treeSet2.iterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
}
}
class MyComparator<T> implements Comparator<T> {
public int compare(T arg0, T arg1) {
if (arg0.equals(arg1)) {
return 0;
}
return ((Comparable<T>) arg0).compareTo(arg1) * -1;
}
}