Set集合:HashSet、TreeSet、泛型

Set集合接口

Set集合的功能及函数和Collection是一致的

常见的子类:

HashSet : 底层数据结构是Hash表,线程是非同步的。保证元素唯一性的原理:判断元素的hashcode值是否相同,如果相同,还会继续判断元素的equals方法是否为真。

TreeSet:可以对Set集合中的元素进行排序。实现Comparable接口后,对象才具有比较性。复写compareTo()方法进行比较。

 

 HashSet

HashSet是怎样保证对象的唯一性的?

首先,对象存储是会对比hashCode值,值相同再用equals来对比对象是否相同。

一般开发中都会复写hashcode, equals方法,用于确认唯一性。

为了保证hashcode的唯一性,可多加一个运算。

注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCodeequals方法。

 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的父类型。下限。  可直接写成<父类型>

你可能感兴趣的:(Set集合:HashSet、TreeSet、泛型)