Set集合接口
Set集合的功能及函数和Collection是一致的
常见的子类:
HashSet : 底层数据结构是Hash表,线程是非同步的。保证元素唯一性的原理:判断元素的hashcode值是否相同,如果相同,还会继续判断元素的equals方法是否为真。
TreeSet:可以对Set集合中的元素进行排序。实现Comparable接口后,对象才具有比较性。复写compareTo()方法进行比较。
HashSet
HashSet是怎样保证对象的唯一性的?
首先,对象存储是会对比hashCode值,值相同再用equals来对比对象是否相同。
一般开发中都会复写hashcode, equals方法,用于确认唯一性。
为了保证hashcode的唯一性,可多加一个运算。
注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
TreeSet
为什么存入一个对象至TreeSet运行时不会报错,而存入一个以上会报错:
因为TreeSet是可以排序的,但没有排序的方式。TreeSet存入的对象是必须要有比较性的,只要实现Comparable接口,复写compareTo()方法才具有比较性。
复写compareTo()方法的时候,注意:
判断时,当比较相等,如果返回0的话,系统认为此对象与对比的对象是相等的,所以不会将对象再加入至TreeSet中。
String类本身就实现了Comparable接口。
记住:排序时,当主要条件相同时,一定要判断下次要条件。
底层的数据结构:二叉树,保证元素唯一性的依据:compareTo方法return 0;
取值时默认顺序:从小到大的顺序
怎么存入的,怎么取出来:只需在compareTo()中 return 1;
TreeSet第一种比较方式:
让元素自身具有比较性,元素需要实现comparable接口,覆盖compareTo()方法,这种方式称为元素的自然排序,或者叫默认排序。
TreeSet第二排序方式:
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。
自定义一个比较器的类,实现Comparator接口,覆盖compare方法。将此比较器的对象放入集合初始化时的参数中。数字及字符串的对象自身具有compareTo方法。
当两种排序都存在时,以比较器为主。
实例:
import java.util.*;
class Student implements Comparable{
String name;
int age;
String sex;
Student(String name,int age,String sex){
this.name=name;
this.age= age;
this.sex= sex;
}
public int compareTo(Object obj){
if(!(obj instanceof Student))
throw new RuntimeException("不是学生对象");
Student stu = (Student)obj;
if(this.age>stu.age)
return 1;
else if(this.age==stu.age)
return this.name.compareTo(stu.name);
else
return -1;
}
}
class TreeSetDemo{
public static void main(String[] args){
TreeSet ts = new TreeSet(new myComp());
ts.add(new Student("zhangsan",13,"mm"));
ts.add(new Student("lisi",5,"vv"));
ts.add(new Student("wangwu",3,"mm"));
ts.add(new Student("zhangsan",21,"mm"));
Iterator it = ts.iterator();
while(it.hasNext()){
sop(it.next());
}
}
public static void sop(Object obj){
Student s = (Student)obj;
System.out.println(s.name+":"+s.sex+":"+s.age+":"+s.hashCode());
}
}
class myComp implements Comparator{
public int compare(Object o1,Object o2){
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
int num = stu1.name.compareTo(stu2.name);
if(num==0)
return new Integer(stu1.age).compareTo(new Integer(stu2.age));
return num;
}
}
泛型:
JDK1.5版本以后出现的新特性,用于解决安全问题,是一个安全机制。
JDK1.5以下,集合内可添加基本数据类型,基本数据类型有自动拆箱装箱功能。
指定集合类所属的数据类型。例:
ArrayList<String> a1 = new ArrayList<String>();
如果不这样定义的话,当存储的类型不一致时,编译时不会报错,但运行时会抛异常。加入泛型后,会把运行时异常转至编译时。让运行时问题减少,安全。 避免了强制转换。
泛型格式:通过<>来定义要操作的引用数据类型。
在使用JAVA提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见。
只要见到<>就要定义泛型。
其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。如同函数传参数一样。
泛型类
定义类时后面加<参数>,
实例化类的时再在泛型中指定类。
什么时候定义泛型类:
当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成扩展。
泛型方法
泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让不同方法可以操作不同的类弄,而且类型还不确定。那么可以将泛型定义在方法上。例:
public <T> void show(T t){
……………………….
}
特殊之处:
静态方法不可以访问类上定义的泛型。如果静态方法操作的应用的数据类型不确定,可定义在该静态方法上。例如:
public static <T> void method(T t){
……………….
}
泛型定义在接口上
当接口为未知的泛型时,实现类也为未知的泛型,可以实例化子类时指定数据类型。
泛型的限定
当两个不同类型的集合类对象,以参数型式传入同一个方法时。该方法中的参数类型为<?>。
不能使用类的特有方法。
?通配符。也可以理解为占位符。
泛型的限定:
? extends E:可以接收E类型或者E的子类型。上限。
? super E:可以接收E类型或者E的父类型。下限。 可直接写成<父类型>