Java集合框架(三)—Set集合

一、Set接口

Set中存放的是唯一,无序的数据(存入和去重的顺序不一定一致)

操作数据的方法与List类似,Set不可以通过下标获取对应位置的元素的值,因为无序的特点,因此Set接口不存在get()方法

Set接口中的实现类包括:HashSet、TreeSet

Set set = new HashSet();
set.add("123");
set.add(1);
set.add(false);
set.add("123");
System.out.println(set);


结果:
[1, 123, false]

1.1 HashSet

HashSet:采用Hashtable哈希表存储结构

  • 优点:添加速度快,查询速度快,删除速度快
  • 缺点:无序
  • LikedHashSet

           -采用哈希表存储结构,同时使用链表维护次序

           -有序(添加顺序)

设置元素的时候,如果是自定义对象,会查找对象中的equals和hashCode方法,如果没有,比较的是地址。因此需要注意将这两个方法加上

定义一个Peron实体类,Person类中未添加equals()和hashCode()方法

public class Person {
    
    private String name;
    private  int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

// 添加equals和hashCode
   @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

测试类将结果进行打印,可知得出三条结果,重写equals和hashCode方法,在将结果进行打印可得出两条

HashSet  hashSet = new HashSet();
hashSet.add(new Person("张三",13));
hashSet.add(new Person("张三",13));
hashSet.add(new Person("李四",12));
System.out.println(hashSet);

结果:
[com.kmm.Person@74a14482, com.kmm.Person@4554617c, com.kmm.Person@1540e19d]

二次结果:
[com.kmm.Person@16e8e05, com.kmm.Person@18e5420]

总结:HashSet是如何保证元素的唯一性的?

      是通过元素的两个方法,hashCode和equals方法来完成

      如果元素的HashCode值相同,才会判断equals是否为true

      如果元素的hashCode值不相同,不会调用equals方法

1.2 TreeSet

TreeSet:底层的实现是TreeMap,利用红黑树来进行实现

  • 优点:有序(排序后的升序),查询速度比List快
  • 缺点:查询速度没有HashSet快

树中的元素是要默认进行排序操作的,如果是基本数据类型,自动比较,如果是引用类型,需要自定义比较器。

       比较器分类:

             内部比较器:定义在元素的类中,通过实现Comparable接口来进行实现

             外部比较器:定义在当前类中,通过实现Comparator接口来实现,但是要将该比较器传递到集合中

  •  基本数据类型的TreeSet实现,满足其有序且排序后升序的特点。默认进行排序操作
TreeSet treeSet = new TreeSet();
treeSet.add(1);
treeSet.add(3);
treeSet.add(2);
treeSet.add(4);
System.out.println(treeSet);

结果:
[1, 2, 3, 4]
  • 自定义比较器,使用内部比较器实现,在Person类中实现Comparable接口并实现其compareTo()方法。根据name的长度从大到小排序
public class Person implements Comparable {
 
    ......
   
    .......

    /**
     * 根据name的长度进行排序
     * @param o
     * @return
     */
    @Override
    public int compareTo(Object o) {
          Person person = (Person)o;
          if (person.name.length()>this.name.length()){
                return 1;
          }else if(person.name.length()<this.name.length()){
                return -1;
          }else {
                return 0;
          }
     }

}

测试类部分代码如下:

TreeSet treeSet = new TreeSet();
treeSet.add(new Person("zhangsan",13));
treeSet.add(new Person("zhangsan",13));
treeSet.add(new Person("lisi",12));
treeSet.add(new Person("wangwu",12));
treeSet.add(new Person("maliu",12));
System.out.println(treeSet);

结果:
[Person{name='zhangsan', age=13}, Person{name='wangwu', age=12}, Person{name='maliu', age=12}, Person{name='lisi', age=12}]

若想将结果按name长度从小到大进行排序,只需要将return返回值交换即可。

  • 自定义比较器,使用外部比较器,在测试类中实现Comparator接口,实现其compare(Object o1, Object o2)方法。此处一定要注意,要将该比较器传递到集合中
public class TreeSetDemo implements Comparator<Person> {

    public static void main(String[] args) {

        TreeSet treeSet = new TreeSet(new TreeSetDemo());
        treeSet.add(new Person("zhangsan",13));
        treeSet.add(new Person("zhangsan",14));
        treeSet.add(new Person("lisi",12));
        treeSet.add(new Person("wangwu",10));
        treeSet.add(new Person("maliu",15));
        System.out.println(treeSet);
    }

    @Override
    public int compare(Person o1, Person o2) {
        if (o1.getAge()>o2.getAge()){
            return 1;
        }else if(o1.getAge()<o2.getAge()){
            return -1;
        }else {
            return 0;
        }

    }
}

注意:

        外部比较器可以定义成一个工具类,此时所有需要比较的规则如果一致的话,可以复用,而内部比较器只有在存储当前对象的时候才可以使用。

        如果两个比较器同时存在,会默认使用外部比较器。

        当使用比较器的时候,不会调用equals方法

二、泛型

当做一些集合的统一操作时,需要保证集合的类型是统一的,此时需要泛型来进行限制。

2.1 特点

1、数据安全

2、获取数据时效率比较高

给集合中的元素设置相同的类型就是泛型的基本需求

使用:

在定义对象的时候,通过<>中设置合理的类型来进行实现

List<String> list = new ArrayList<String>();
list.add("1");
list.add("abc");
list.add("true");
list.add(new Person("zhangsan",12).toString());
System.out.println(list);

2.2 泛型的高阶应用

1、泛型类

     当定义类的时候在类名的后面添加<E、V、K、A...>等,起到占位的作用,类中的方法的返回值类型和属性的类型都可以使用

2、泛型接口

    当定义接口的时候,在接口的名称后添加<E、V、K、A...>等

    1、子类在进行实现的时候,可以不填写泛型的类型,此时在创建具体的子类对象的时候才决定使用什么类型

     2、子类在实现泛型接口的时候,只在实现父类的接口的时候指定父类的泛型类型即可,此时,测试方法中的泛型类型必须要跟子类保持一致

3、泛型方法

在定义方法的时候,指定方法的返回值和参数是自定义的占位符,可以是父类名中的T,也可以是自定义的Q,只不过在使用Q的是需要使用<Q>定义在返回值的前面

4、泛型的上限(工作中不用):如果父类确定了,所有的子类都可以直接使用

5、泛型的下限(工作中不用):如果子类确定了,子类所有的父类都可以直接传到参数使用

posted @ 2020-12-03 17:01  豆小豆1314  阅读(154)  评论(0编辑  收藏  举报