Java基础之集合框架系列(三)


前言:本文介绍Set系列集合两种常用结构,HashSet以及TreeSet集合

提前说明:Set系列集合具有的共性方法与Collection完全一致,本文不再额外列举
可以参考:菜鸟猿大战Java之集合框架系列(一)

1.HashSet

1.概述

HashSet的底层数据结构是哈希表,哈希表按照元素的哈希值大小顺序进行存储,而非元素的实际存入顺序。HashSet是非线程同步的

哈希表特点: 存储时先比较两元素哈希值是否相同,若相同,再比较两元素是否为同一对象[利用元素自身的equals方法]

2.常用方法

构造方法:

HashSet()
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。

HashSet(Collection<? extends E>c)
构造一个包含指定 collection 中的元素的新 set。

HashSet(int initialCapacity)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75 )。

HashSet(int initialCapacity, float loadFactor)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。

特性方法:

Object clone()
返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。
说明:浅表复制就是仅复制类中的值类型成员,深表复制就是复制类中的值类型成员和引用类型成员。

3.利用HashSet存储自定义对象

场景描述:自定义Teacher类,将其多个实例化对象加入HashSet集合对象中,且要求不能存入多个相同元素。之后演示元素的取出操作。

步骤:

1.复写自定义对象的hashCode)()方法(本例中可以返回姓名字符串的hashCode加上年龄组成的int数据),保证每个对象哈希值的唯一性 (也可以在相加之前进行age*39之类的操作,尽量保证最终返回哈希值的唯一性,提高效率)

2.复写自定义对象的equals()方法(本例可认为年龄和姓名一致的两个对象为一个对象),以保证HashSet中的元素唯一性

示例代码:

import java.util.HashSet;
import java.util.Iterator;

//自定义作为存储对象的Teacher类
class Teacher{
    private String name;
    private int age;    

    //构造方法
    public Teacher(String name,int age){
        this.name=name;
        this.age=age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }


    //重写equals方法
    public boolean equals(Object o){
        if(!(o instanceof Teacher))
            return false;
        //将传入对象强转为Teacher对象
        Teacher t=(Teacher)o;
        if(t.getAge()==this.age&&t.getName().equals(this.name))
            return true;
        else
            return false;
    }

    //重写hashCode方法
    public int hashCode(){
        return name.hashCode()+age*39;
    }

}

//测试用类
public class HashSetDemo {

    public static void main(String[] args) {
        //新建HashSet集合装载Teacher对象
        HashSet<Teacher> hs=new HashSet<Teacher>();
        hs.add(new Teacher("bill",20));
        hs.add(new Teacher("bill",20));
        hs.add(new Teacher("tom",30));
        hs.add(new Teacher("gary",25));
        hs.add(new Teacher("gary",25));
        hs.add(new Teacher("sythen",20));

        //利用迭代器对HashSet中的元素进行取出
        Iterator<Teacher> it=hs.iterator();
        while(it.hasNext()){
            Teacher t=(Teacher)it.next();
            System.out.println("姓名:"+t.getName()+"----"+"年龄:"+t.getAge());
        }

    }

}

程序运行结果:

姓名:gary—-年龄:25
姓名:sythen—-年龄:20
姓名:tom—-年龄:30
姓名:bill—-年龄:20

说明:从元素的取出结果可以看出HashSet集合的元素取出顺序与其存入顺序并不相同。

总结:
1.HashSet保证元素唯一性的方法:通过元素的两个方法,hashCode和equals()来完成。若元素的hashCode值相同,才会调用equals()判断是否为true;若元素的hashCode值不同,则不会调用equals()方法

2.实际开发中要放置于集合中的自定义对象一般都要复写hashCode()和equals()方法!!

3.HashSet判断元素是否存在和删除元素等操作,依赖的方法也是是元素的hashCode()equals()方法

2.TreeSet

1.概述

特点:
1.可以对set集合中的元素进行排序,但并不一定是加入顺序,默认为根据元素的自然顺序排序
2.TreeSet底层数据结构:二叉树
3.保证元素唯一性的依据:compareTo()方法返回值为0

2.常用方法

构造方法

TreeSet()
构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。

TreeSet(Collection<? extends E> c)
构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。

TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。

TreeSet(SortedSet<E> s)
构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。

特性方法:

E ceiling(E e)
返回此 set 中大于等于给定元素的最小元素;如果不存在这样的元素,则返回 null。
E floor(E e)
返回此 set 中小于等于给定元素的最大元素;如果不存在这样的元素,则返回 null。

E higher(E e)
返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 null。
E lower(E e)
返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 null。

E first()
返回此 set 中当前第一个(最低)元素。
E last()
返回此 set 中当前最后一个(最高)元素。

E pollFirst()
获取并移除第一个(最低)元素;如果此 set 为空,则返回 null。
E pollLast()
获取并移除最后一个(最高)元素;如果此 set 为空,则返回 null。

Object clone()
返回 TreeSet 实例的浅表副本。

Comparator<? super E> comparator()
返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null。

Iterator<E> descendingIterator()
返回在此 set 元素上按降序进行迭代的迭代器。

NavigableSet<E> descendingSet()
返回此 set 中所包含元素的逆序视图。

SortedSet<E> headSet(E toElement)
返回此 set 的部分视图,其元素严格小于 toElement。
NavigableSet<E> headSet(E toElement, boolean inclusive)
返回此 set 的部分视图,其元素小于(或等于,如果 inclusive 为 true)toElement。

SortedSet<E> tailSet(E fromElement)
返回此 set 的部分视图,其元素大于等于 fromElement。
NavigableSet<E> tailSet(E fromElement, boolean inclusive)
返回此 set 的部分视图,其元素大于(或等于,如果 inclusive 为 true)fromElement。

NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
返回此 set 的部分视图,其元素范围从 fromElement 到 toElement。
SortedSet<E> subSet(E fromElement, E toElement)
返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。

3.TreeSet存储自定义对象

场景描述:
自定义Teacher类,将其多个实例化对象加入TreeSet集合对象中,且要求不能存入多个相同元素。存入过程需要根据Teacher对象的年龄进行排序,之后演示元素的取出操作。

Comparable此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为自然排序,类的 compareTo 方法被称为它的自然比较方法。

自然比较方法:

int compareTo(T o)
比较此对象与指定对象的顺序。
返回:负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。

说明:

1.要存入TreeSet中的自定义对象必须实现Comparable接口,以便具有可排序性;并且为了获取有意义的排序方式应该在类中实现compareTo()方法

2.当compareTo方法返回0时,在该集合中认为进行比较的两个对象为同一对象,两者仅有第一个会存入TreeSet

3.针对条目二中的情况,应该在主要条件之外添加次要判断条件(如本例中的姓名排序[使用字符串的compareTo(String s)方法])

4.以上是TreeSet实现可排序的第一种方式,即让元素本身直接具备可比较性

示例代码:


import java.util.Iterator;
import java.util.TreeSet;

class Teacher implements Comparable<Teacher>{
    private String name;
    private int age;

    //构造方法
    public Teacher(String name,int age){
        this.name=name;
        this.age=age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    //重写compareTo方法
    public int compareTo(Teacher t) {        
        if(this.age>t.getAge())
            return 1;
        else if(this.age<t.getAge())
            return -1;
        else{
            //当两个对象年龄相同时将姓名作为次级排序依据
            return this.name.compareTo(t.getName());
        }
    }

}

//测试用类
public class TreeSetDemo {

    public static void main(String[] args) {
        //新建TreeSet对象装载Teacher对象
        TreeSet<Teacher> ts=new TreeSet<Teacher>();
        ts.add(new Teacher("bill",20));
        ts.add(new Teacher("bill",20));
        ts.add(new Teacher("tom",30));
        ts.add(new Teacher("gary",25));
        ts.add(new Teacher("gary",25));
        ts.add(new Teacher("sythen",20));

        //利用迭代器进行取出
        Iterator<Teacher> it=ts.iterator();
        while(it.hasNext()){
            Teacher t=(Teacher)it.next();
            System.out.println("姓名:"+t.getName()+"----"+"年龄:"+t.getAge());
        }
    }

}

程序运行结果:

姓名:bill—-年龄:20
姓名:sythen—-年龄:20
姓名:gary—-年龄:25
姓名:tom—-年龄:30

4.二叉树

目的:减少比较次数,提高执行效率

说明:

1.利用二叉树的特点,只要将compareTo()方法实现为每次返回1,则取出时的顺序为就为元素存入的顺序

2.同理,只要将compareTo方法实现为每次返回-1,则取出时的顺序就为为元素存入顺序的反序

5.实现Comparator方式排序

使用场景:当元素本身不具备比较性时,或者其具备的比较性不是业务所需要的,这时就需要让集合本身具备比较性。定义一个比较器类实现Comparator接口,并复写compare()方法。将比较器对象作为参数传递给TreeSet的构造函数。

在集合初始化时,就让其拥有比较方式:
使用到的TreeSet构造方法:

TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序

说明:

1.在自定义比较器类中实现Comparator接口,在该类中重写compare(Object o1,Object o2)方法
2.实例化该自定义对象,作为参数新建TreeSet集合对象
3.也可以使用匿名内部类的方式对TreeSet的构造函数传递参数
4.这也是实现TreeSet排序的第二种方式,即利用比较器实现集合内元素的排序

总结:
1.其实这就是把原来在自定义类中对compareTo()方法的重写转移到集合的实例化过程中。即将相关的判断过程写在实现了Comparator接口的自定义类的compare方法中
2.若两种排序都存在,则程序会以比较器的排序方式为主
3.基本类型(如int)的包装类(Interger)也实现了comparable方法

需求描述:通过比较器类实现Teacher对象按照姓名排序,次级排序依据为Teacher对象的年龄大小。

示例代码:

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

//...省略Teacher类

//自定义比较器类
class OwnComparator implements Comparator<Teacher>{

    //重写compare方法
    public int compare(Teacher o1, Teacher o2) {
        if(o1.getName().equals(o2.getName()))
            return new Integer(o1.getAge()).compareTo(new Integer(o2.getAge()));
        return o1.getName().compareTo(o2.getName());
    }

}

//自定义测试类
public class OwnComparatorDemo {

    public static void main(String[] args) {        
        //实例化自定义选择器对象
        OwnComparator oc=new OwnComparator();
        //实例化TreeSet承载Teacher对象
        TreeSet<Teacher> ts=new TreeSet<Teacher>(oc);
        ts.add(new Teacher("bill",20));
        ts.add(new Teacher("bill",20));
        ts.add(new Teacher("tom",30));
        ts.add(new Teacher("gary",25));
        ts.add(new Teacher("gary",25));
        ts.add(new Teacher("sythen",20));
        //利用Iterator对元素进行取出
        Iterator<Teacher> it=ts.iterator();
        while(it.hasNext()){
            Teacher t=it.next();
            System.out.println("姓名"+t.getName()+"----"+"年龄"+t.getAge());
        }
    }

}

程序运行结果:

姓名:bill—-年龄:20
姓名:gary—-年龄:25
姓名:sythen—-年龄:20
姓名:tom—-年龄:30

6.TreeSet使用实例

场景描述:将字符串存入TreeSet中,并按照字符串长度排序

说明字符串本身具备排序性(即已经实现了comparable接口),但其排序方式并不是本例所需要的,因此可以使用比较器进行自定义排序。但是可以将字符串本身具有的compareTo()方法作为次要排序条件。

示例程序:

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

//自定义String比较器类
class StringComparator implements Comparator<String>{

    //重写compare方法
    public int compare(String s1, String s2) {
        if(s1.length()>s2.length())
            return 1;
        else if(s1.length()<s2.length())
            return -1;
        else{
            //当用于比较的两个字符串长度相等时返回字符串的默认排序返回值
            return s1.compareTo(s2);
        }
    }

}

//自定义测试类
public class StringComparatorDemo {

    public static void main(String[] args) {
        //新建自定义字符串比较器对象
        StringComparator sc=new StringComparator();
        //新建TreeSet对象承载字符串
        TreeSet<String> ts=new TreeSet<String>(sc);
        ts.add("java");
        ts.add("python");
        ts.add("php");
        ts.add("c#");
        ts.add("c++");
        ts.add("jsp");
        //利用迭代器对元素进行输出
        Iterator<String> it=ts.iterator();
        while(it.hasNext()){
            System.out.println(it.next()); 
        }

    }

}

程序运行结果:

c#
c++
jsp
php
java
python


相关阅读:
菜鸟猿大战Java之集合框架系列(一)
菜鸟猿大战Java之集合框架系列(二)
菜鸟猿大战Java之集合框架系列(四)
菜鸟猿大战Java之集合框架系列(五)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值