从Java集合汇总开始看起吧,重父类的Collection讲起。
1.2:Set接口
无序的,不可重复,允许null值。
无序性:指的是元素底层存储的位置是无序的。不是打印出来的值的顺序。
不可重复性:当向Set中添加重复值的时候,后面的值添加不进去。
要求:往set中添加的元素,其所在类一定要重写equals() 和 hashCode() 方法,进而保证Set中元素的无序性和不可从重复性。
Set中的元素如何存储?使用了哈希算法。
* 向Set中添加对象时,首先调用此对象所在类的hashCode() 方法,计算此对象的哈希值,此哈希值决定了对象在Set中存储的位置。
* 如若此位置之前没有对象存储,则这个对象直接存储到此位置。
* 如果此位置已有对象储存,再通过equals() 比较这两个对象是否相同,如果相同,后一个对象就不添加进来。
* 要求:hashCode() 方法,和equals()方法一致,意思如下:
* 如果两个元素计算出来的 hashCode()返回值不一样,则equals()结果也要不一样
* 如果两个元素计算出来的 hashCode()返回值一样,则equals()结果也要一样
三个实现类HashSet,LinkedHashSet,TreeSet
1.2.1 HashSet类介绍
@Test
public void testHashSet() {
Set set = new HashSet();
set.add(123);
set.add(456);
set.add("张三");
set.add(new String("张三"));
set.add(null);
set.add(951);
set.add(new Person("小王", 15));
set.add(new Person("小李", 14));
// 如果不重写person中的equals()方法,和hashCode()方法,下面new的Person依旧可以添加进Set。
set.add(new Person("小李", 14));
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
1.2.2 LinkedHashSet
LinkedHashSet是HashSet子类,同时也实现了 Set接口。
* LinkedHashSet:使用链表维护了元素添加进集合的添加顺序。
* LinkedHashSet 根据元素的hashCode值来决定元素的存储位置,
* 但同时使用链表维护元素的次序,这使得元素看起来是插入顺序保存的。
* 导致我们遍历LinkedHashSet集合元素时,是按照添加顺序遍历的。
* 元素存储的是无序的,只是用链表连接起来了,显的有序。
*
* LinkedHashSet插入性能略低于 HashSet( 因为要维护链表 )
* 但是迭代访问Set中全部元素性能较好( 也因为有链表存在 )
*
* LinkedHashSet 不允许集合元素重复。
@Test
public void TestLinkedHashSet() {
Set set = new LinkedHashSet();
set.add(985);
set.add(211);
set.add(211);
set.add("张三");
set.add(new Person("老王", 17));
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
1.2.3 TreeSet
* TreeSet
* 1.只能添加同类型元素
* 2.相同元素不会重复添加
* 3.添加进集合的元素,可以人为指定顺序进行遍历,像String,包装类等,默认按照从小到大的顺序遍历
* 4.向TreeSet中添加自定义对象时,有两种排序方法:自然排序 和 定制排序
* 5.向TreeSet中添加元素时,首先按照compareTo()进行比较,一旦返回0,表示两元素相同。
* 也许仅是两个对象的此属性相同(可能此对象有多个属性),但程序会认为这两个对象相同,进而后一个对象不能添加进来。
*
* CompareTo() 与 hashCode() 以及 equals() 三者保持一致。
*
* 自然排序和定制排序的选择:
* 举例:如果要把Person类add到TreeSet中,但是Person类你无法修改(比如你没权限动这个类),就选择定制排序,如果你可以操作Person类,则可以选自然排序。
* 定制排序优先级较高,如果同时写了自然排序和定制排序,最终按照自然排序实现。
/**
* String,包装类等得默认排序
*/
@Test
public void testTreeSet() {
Set set = new TreeSet();
set.add("BB");
// 之前添加的是String,这里再添加int就会报错。
// set.add(123);
set.add("GG");
set.add("ZZ");
set.add("YY");
set.add("AA");
set.add("AA");
for (Object o : set) {
System.out.println(o);
}
}
/**
* 自然排序
* 然排序:要求自定义类实现java.long.Comparable接口,并重写其compareTo(Object obj)方法,意在指明按照哪个属性排序
*/
@Test
public void testTreeSet1() {
Set set = new TreeSet();
// 如果Person类没有实现Comparable接口时,向TreeSet中添加Person对象时,报类转换异常:java.lang.ClassCastException
set.add(new Person("AA", 12));
set.add(new Person("GG", 17));
set.add(new Person("BB", 10));
set.add(new Person("XX", 9));
set.add(new Person("XX", 10));
for (Object o : set) {
System.out.println(o);
}
}
/**
* 定制排序
*/
@Test
public void testTreeSet2() {
Comparator com = new Comparator() {
// 1. 向TreeSet中添加Customer类对象,再此compare()方法中,指明按照Custumer哪个属性排序
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Customer && o2 instanceof Customer) {
Customer c1 = (Customer) o1;
Customer c2 = (Customer) o2;
// 单独根据id排序
// return c1.getId().compareTo(c2.getId());
// 为了让CompareTo() 与 hashCode() 以及 equals() 三者保持一致。
int i = c1.getId().compareTo(c2.getId());
if (i == 0) {
return c1.getName().compareTo(c2.getName());
}
}
return 0;
}
};
// 2.将此对象作为形参,传递给TreeSet的构造器
TreeSet set = new TreeSet(com);
// 3.向TreeSet中添加对象【必须是Comparator接口中Compare方法中设计的类对象】
set.add(new Customer(15, "AA"));
set.add(new Customer(14, "DD"));
set.add(new Customer(13, "BB"));
set.add(new Customer(5, "XX"));
for (Object o : set) {
System.out.println(o);
}
// 每次往TreeSet中添加对象时,都是自动去找Comparator接口实现类的compare方法,进行比较排序。
}
```