现在的位置: 首页 > 综合 > 正文

Java集合学习之Collection(2)

2018年05月26日 ⁄ 综合 ⁄ 共 3774字 ⁄ 字号 评论关闭

上篇博客讲了Collection接口的一些基本操作,这篇博客主要介绍Collection接口的子接口Set。
Set是一种无序的集合,其基本操作和Collection接口是差不多的,主要的不同点在于Set中不能重复元素而Collection集合是可以的。对于Set集合我们主要关心它的HashSet,TreeSet两个实现类。

一.HashSet
HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个类;HashSet通过hash算法确定元素的存储位置。值得注意的是HashSet中可以存储值为null的元素。
那么HashSet是怎样工作的呢?我们每传入一个元素,HashSet就调用该对象的HashCode()方法计算得到该对象的HashCode值,然后就根据这个值决定该元素在HashSet中的存储位置。如果两个对象通过equals()比较相等,但是计算出的HashCode值不相同,那么HashSet就将他们存储到不同的位置上去;如果两个对象的HashCode值相等,而通过equals()方法比较不相等,那么HashSet就将他们在同一个位置上以链表的形式存储。
综上我们可以知道HashSet判断两个对象相等的标准是:两个对象的HashCode值相同,并且两个对象通过equals()方法比较返回true。所以这两个方法对HashSet来说是十分重要的,我们需要保证当两个对象的HashCode值一样时,其通过equals()方法比较也会返回true。下面的程序中A,B,C类分别重写了自己的HashCode()和equals()方法。

public class A {
    ///重写A类的equals()方法,使其总是返回true
    public boolean equals(Object obj){
        return true;
    }
}

public class B {

    ///重写B类的HashCode()方法,使其总是返回2
    public int hashCode(){
        return 2;
    }
}


public class C {

    ///重写C类的equals()方法,使其总是返回true
    public boolean equals(Object obj){
        return true;
    }
    ///重写C类的HashCode()方法,使其总是返回3
    public int hashCode(){
        return 3;
    }
}


public class HashCodeTest {

    public static void main(String[] args){
        HashSet st = new HashSet();
        //依次添加两个A,B,C类对象
        st.add(new A()); 
        st.add(new A());
        st.add(new B());
        st.add(new B());
        st.add(new C());
        st.add(new C());
        //可以看到只输出了一个C类,而A,B类都输出了两个
        System.out.println(st);
    }
}

HashSet有一个叫LinkedHashSet的子类,LinkedHashSet的基本原理和HashSet是一样的,只是LinkedHashSet内部通过一个链表来维护插入元素的相对顺序,这样使得看起来元素是以插入的顺序保存的;当我们遍历LinkedHashSet时就可以将元素以其输入顺序输出了。

package lkl;

import java.util.LinkedHashSet;

public class LinkedHashSetTest {

    public static void main(String[] args){

        LinkedHashSet lt = new LinkedHashSet();
        lt.add("java");
        lt.add("c");
        lt.add("c++");
        lt.add("shell");
        ///从下面两次输出可以看出,元素总是以输入顺序输出
        System.out.println(lt);
        lt.remove("java");
        lt.add("java");  ///从新添加后java被放到最后
        System.out.println(lt);
    }
}

二.TreeSet
TreeSet是Set的另一个重要的子类。与HashSet通过hash的方式确定元素的存储位置不同,TreeSet内部通过一棵红黑树来维护元素的相对次序;由于TreeSet中元素的有序性,相对HashSet,还提供了几个额外的方法,如下面的代码所示:

package lkl;

import java.util.TreeSet;

public class TreeSetTest {

public static void main(String[] args){

    TreeSet ts = new TreeSet();
    ts.add(1);
    ts.add(-2);
    ts.add(7);
    ts.add(4);

    ///从下面的输出可以看出TreeSet是有序的
    System.out.println(ts);

    ///Comparator comparator()
    ///如果TreeSet采用了定制排序,则返回定制排序的Comparator
    ///如果采用了自然排序,则返回null
    System.out.println(ts.comparator());

    ///Object first():返回集合中第一个元素
    ///Object last():返回集合中最后一个元素
    System.out.println("集合中第一个元素为: "+ts.first());
    System.out.println("集合中最后一个元素为: "+ts.last());

    ///Object lower(Object e):返回集合中第一个小于e的元素,e不一定在集合中
    ///Object higher(Object e):返回集合中第一个大于e的元素
    ///如果没有元素满足的话,返回null
    System.out.println("集合中第一个小于5的元素是: "+ts.lower(5));
    System.out.println("集合中第一个大于5的元素是: "+ts.higher(5));

    ///SortedSet subSet(Object e1,Object e2):返回Set从e1-e2之间的子集
    System.out.println(ts.subSet(1,5));

    ///SortSet headSet(Object e):返回集合中小于e的元素组成的子集
    System.out.println(ts.headSet(6));

    ///SortSet tailSet(Object e):返回集合中大于等于e元素组成的子集
    System.out.println(ts.tailSet(1));
}
}

因为TreeSet是有序的,所以插入一个元素时总是要比较大小;这里进行大小比较时总是调用对象obj1.compareTo(Object obj2)方法(返回正整数表示obj1大于obj2,返回负整数时表示obj1小于obj2,返回0时表示相等),只有两个对象通过compareTo()比较返回0时,才认为两个对象相等。这导致一个问题,不同的对象不能被同时插入到TreeSet中,因为不同的对象不能通过comparaTo()方法进行比较;当我们将不同的对象插入到TreeSet中时会引发异常。
如果我们想自己定义排序的规则,我们可以借助Comparator接口。该接口中包含了一个int compare(T t1,T t2)的方法,该方法在t1>t2时返回正整数,在t1

package lkl;

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

public class TreeSetComparatorTest {
///TreeSet原本是从从小到大输出的,现在自己定制排序后
///可以实现从大到小输出
    public static void main(String[] args){
        TreeSet ts = new TreeSet(new Comparator(){
           public int compare(Object o1,Object o2){
                int m1= (int)o1;
                int m2= (int)o2;
                if(m1==m2)
                    return 0;
                if(m1>m2)
                    return -1;
                return 1;
            }
        });
        ts.add(1);
        ts.add(2);
        ts.add(-1);
        System.out.println(ts);
    }
}

最后要强调一句,如果我们向set中添加了可变的对象,然后又改变了该对象中的File,那么可能造成该对象在set中”不可见”的情况;如果我们向set中加入了可变的对象,那么我们不要在后面再改变其中File的值。

抱歉!评论已关闭.