java---HashSet、TreeSet、泛型

Vector

集合 的体系:

–| Collection 单例集合的根接口

—–| List 如果是实现了List接口的集合类,具备的特点: 有序,可重复。

———| ArrayList ArrayList 底层是维护了一个Object数组实现的。 特点: 查询速度快,增删慢。

———| LinkedList LinkedList 底层是使用了链表数据结构实现的, 特点: 查询速度慢,增删快。

———| Vector(了解即可) 底层也是维护了一个Object的数组实现的,实现与ArrayList是一样的,但是Vector是线程安全的,操作效率低。

——| Set 如果是实现了Set接口的集合类,具备的特点: 无序,不可重复。

笔试题: 说出ArrayLsit与Vector的区别?

相同点: ArrayList与Vector底层都是使用了Object数组实现的。
不同点: 
    1. ArrayList是线程不同步的,操作效率高。 
       Vector是线程同步的,操作效率低。
    2. ArrayList是JDK1.2出现,Vector是jdk1.0的时候出现的。

public static void main(String[] args) {
    Vector v  =  new Vector();
    //添加元素
    v.addElement("张三");
    v.addElement("李四");
    v.addElement("王五");
    //迭代该集合
    Enumeration e = v.elements(); //获取迭代器
    while(e.hasMoreElements()){
        System.out.println(e.nextElement());
    }
}

set

public static void main(String[] args) {
    Set set = new HashSet();
    set.add("王五");
    set.add("张三");
    set.add("李四");
    System.out.println("添加成功吗?"+set.add("李四"));
    System.out.println(set);
}

HashSet

——–| HashSet 底层是使用了哈希表来支持的,特点: 存取速度快.

哈希表是一个桶式结构

hashSet的实现原理:

往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,
然后通过元素 的哈希值经过移位等运算,就可以算出该元素在哈希表中 的存储位置。

情况1: 如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。

情况2: 如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次
,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行 添加。


class Person{

int id;

String name;

public Person(int id, String name) {
    super();
    this.id = id;
    this.name = name;
}

@Override
public String toString() {
    return "{ 编号:"+ this.id+" 姓名:"+ this.name+"}";
}

@Override
public int hashCode() {
    System.out.println("=======hashCode=====");
    return this.id;
}


@Override
public boolean equals(Object obj) {
    System.out.println("======equals======");
    Person p = (Person)obj;
    return this.id==p.id;
}
}

public static void main(String[] args) {
/*
    HashSet set = new HashSet();
    set.add("狗娃");
    set.add("狗剩");
    set.add("铁蛋");
    System.out.println("集合的元素:"+ set);
*/  

    HashSet set = new HashSet();
    set.add(new Person(110,"狗娃"));
    set.add(new Person(220,"狗剩"));
    set.add(new Person(330,"铁蛋"));
    //在现实生活中只要编号一致就为同一个人.
    System.out.println("添加成功吗?"+set.add(new Person(110,"狗娃")));
    System.out.println("集合的元素:"+set);

}

public static void main(String[] args) {
    String str1 = "hello";
    String str2 = new String("hello");
    System.out.println("两个是同一个对象吗?"+(str1==str2));
    System.out.println("str1的hashCode:"+ str1.hashCode());//99162322
    System.out.println("str2的hashCode:"+ str2.hashCode());//99162322
    /* 
     * HashCode默认情况下表示的是内存地址,String 类已经重写了Object的hashCode方法了。

        注意: 如果两个字符串的内容一致,那么返回的hashCode 码肯定也会一致的。 

     *
     */
}

TreeSet

TreeSet 如果元素具备自然顺序 的特性,那么就按照元素自然顺序的特性进行排序存储。

public static void main(String[] args) {
    TreeSet tree = new TreeSet();
/*  tree.add(1);
    tree.add(10);
    tree.add(7);
    tree.add(19);
    tree.add(9);*/

    tree.add('b');
    tree.add('f');
    tree.add('a');
    tree.add('c');
    System.out.println(tree);//[a, b, c, f]
}

treeSet要注意的事项:

1. 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
2. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素
的比较规则定义在compareTo(T o)方法上。 

3. 如果比较元素的时候,compareTo方法返回 的是0,那么该元素就被视为重复元素,不允许添加.(注意:TreeSet与HashCode、equals方法是没有任何关系。)

4. 往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序 的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个
比较器。

5.  往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口, 在创建TreeSet对象的时候也传入了比较器
那么是以比较器的比较规则优先使用。
treeSet底层是使用红黑二叉树实现的,存储规则:左小右大

如何自定义比较器:

自定义一个类实现Comparator接口即可,把元素与元素之间的比较规则定义在compare方法内即可。

    自定义比较器的格式 :

        class  类名  implements Comparator{

        }

推荐使用:使用比较器(Comparator)。 

class  Emp implements Comparable<Emp>{

int id;

String name;

int salary;

public Emp(int id, String name, int salary) {
    super();
    this.id = id;
    this.name = name;
    this.salary = salary;
}

@Override
public String toString() {
    return "{ 编号:"+  this.id+" 姓名:"+ this.name+" 薪水:"+ this.salary+"}";
}


//@Override //元素与元素之间的比较规则。
// 负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。 
public int compareTo(Emp o) {
//      System.out.println(this.name+"compare"+ e.name);
    return this.salary- o.salary;
}   
}


//自定义一个比较器
class MyComparator implements Comparator<Emp>{

@Override
public int compare(Emp o1, Emp o2) {
    return o1.id-o2.id;
}

//根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。 
/*@Override
public int compare(Object o1, Object o2) {
    Emp e1 = (Emp) o1;
    Emp e2 = (Emp) o2;
    return e1.id - e2.id;
}*/

}


public static void main(String[] args) {
    //创建一个比较器对象
    MyComparator comparator = new MyComparator();
    //创建TreeSet的时候传入比较器
    TreeSet tree = new TreeSet(comparator);

    tree.add(new Emp(110, "老陆", 100));
    tree.add(new Emp(113, "老钟", 200));
    tree.add(new Emp(220, "老汤", 300));
    tree.add(new Emp(120, "老蔡", 500));
    System.out.println("集合的元素:"+tree);

}

字符串比较

TreeSet是可以对字符串进行排序 的, 因为字符串已经实现了Comparable接口。

字符串的比较规则:

情况一: 对应位置有不同的字符出现, 就比较的就是对应位置不同的字符。

情况 二:对应位置上 的字符都一样,比较的就是字符串的长度。


public static void main(String[] args) {
    TreeSet tree = new TreeSet();
    tree.add("abcccccccccccccccccc");
    tree.add("abc");
    System.out.println(tree);//[abc, abcccccccccccccccccc]

    System.out.println("abw".compareTo("abcccccccccccc"));//20

}

泛型

泛型的好处:

1. 将运行时的异常提前至了编译时。
2. 避免了无谓的强制类型转换 。

泛型在集合中的常见应用:

ArrayList<String>  list = new ArrayList<String>();  true     推荐使用。

ArrayList<Object>  list = new ArrayList<String>();  false
ArrayList<String>  list = new ArrayList<Object>();  false

//以下两种写法主要是为了兼顾新老系统的兼用性问题。
 * 
ArrayList<String>  list = new ArrayList();           true   

ArrayList    list = new ArrayList<String>();   true   

注意: 泛型没有多态的概念,左右两边的数据 类型必须 要一致,或者只是写一边的泛型类型。

推荐使用: 两边都写泛型。

自定义泛型

自定义泛型: 自定义泛型就是一个数据类型的占位符或者是一个数据类型的变量。

方法上自定义泛型:

修饰符     <声明自定义的泛型>返回值类型    函数名(使用自定义泛型 ...){

}

在泛型中不能使用基本数据类型,如果需要使用基本数据类型,那么就使用基本数据类型对应的包装类型。

byte----> Byte
short---> Short 
 int----> Integer
 long----> Long 
 double ----> Double 
 float -----> Float
 boolean-----Boolean
 char-------》 Character 

方法泛型注意的事项:

1. 在方法上自定义泛型,这个自定义泛型的具体数据类型是在调用该 方法的时候传入实参时确定具体的数据类型的。
2. 自定义泛型只要符合标识符 的命名规则即可, 但是自定义泛型我们一般都习惯使用一个大写字母表示。  T Type  E Element

public static void main(String[] args) {
    String str = getData("abc");
    Integer i = getData(123);
}

public static <T>T getData(T o){

    return o;
}

泛型类

泛型类的定义格式:

class 类名<声明自定义泛型>{

}

泛型类要注意的事项:

1. 在类上自定义泛型的具体数据类型是在使用该类的时候创建对象时候确定的。
2. 如果一个类在类上已经声明了自定义泛型,如果使用该类创建对象 的时候没有指定 泛型的具体数据类型,那么默认为Object类型
3.在类上自定义泛型不能作用于静态的方法,如果静态的方法需要使用自定义泛型,那么需要在方法上自己声明使用。

class MyArrays<T>{

//元素翻转
public void reverse(T[] arr){
    for(int startIndex = 0, endIndex = arr.length-1 ; startIndex<endIndex ; startIndex++,endIndex--){
        T temp  = arr[startIndex];
        arr[startIndex] = arr[endIndex];
        arr[endIndex] = temp;
    }
}

//
public String toString(T[] arr){
    StringBuilder sb = new StringBuilder();
    for(int i = 0 ; i < arr.length ; i++){
        if(i==0){
            sb.append("["+arr[i]+",");
        }else if(i==arr.length-1){
            sb.append(arr[i]+"]");
        }else{
            sb.append(arr[i]+",");
        }
    }
    return sb.toString();
}

public static <T>void print(T[] t){

}
}

public static void main(String[] args) {
    Integer[] arr = {10,12,14,19};

    MyArrays<Integer> tool = new MyArrays<Integer>();
    tool.reverse(arr);
    System.out.println("数组的元素:"+tool.toString(arr));

    MyArrays<String> tool2 = new MyArrays<String>();
    String[] arr2 = {"aaa","bbb","ccc"};
    tool2.reverse(arr2);        

    ArrayList<String> list = new ArrayList<String>();
}

泛型接口

泛型接口的定义格式:

interface 接口名<声明自定义泛型>{

}

泛型接口要注意的事项:

1. 接口上自定义的泛型的具体数据类型是在实现一个接口的时候指定 的。
2. 在接口上自定义的泛型如果在实现接口的时候没有指定具体的数据类型,那么默认为Object类型。

需求: 目前我实现一个接口的时候,我还不明确我目前要操作的数据类型,我要等待创建接口实现类 对象的时候我才能指定泛型的具体数据类型。

如果要延长接口自定义泛型 的具体数据类型,那么格式如下:
格式:  
public class Demo4<T> implements Dao<T>{

}

interface Dao<T>{

public void add(T t);   
}

public class Demo4<T> implements Dao<T> {

public static void main(String[] args) {
    Demo4<String> d = new Demo4<String>();
}

public void add(T t){

}
}

泛型上下限

需求1: 定义一个函数可以接收接收任意类型的集合对象, 要求接收的集合对象只能存储Integer或者是Integer的父类类型数据。

需求2: 定义一个函数可以接收接收任意类型的集合对象, 要求接收的集合对象只能存储Number或者是Number的子类类型数据。

泛型中通配符: ?

? super Integer : 只能存储Integer或者是Integer父类元素。  泛型 的下限

? extends Number : 只能存储Number或者是Number类型的子类数据。 泛型上限

public static void main(String[] args) {
    ArrayList<Integer> list1 = new ArrayList<Integer>();
    ArrayList<Number> list2 = new ArrayList<Number>();

    HashSet<String> set = new HashSet<String>();
    //getData(set);

}

//泛型的上限
public static void getData(Collection<? extends Number> c){


}

//泛型的下限
public static void print(Collection<? super Integer> c){

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值