A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 章成 黑马帝   /  2011-7-14 21:08  /  4140 人查看  /  5 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

自己知道集合Set中HashSet与TreeSet,要重写,但不懂底层代码是什么时候用?
是add()时每加一个新元素都跟前面的比一下,这样来避免重复,还是说在最后Iteractor遍历输出时把重复的挡着不输出来,这样达到没有重复的效果?

5 个回复

倒序浏览
黑马网友  发表于 2011-7-14 22:31:54
沙发
add写入的时候就找了原集合中了,有重复的就不再新加,而是更改。
至于你说的底层代码什么时候用,我没听明白是什么意思,观望
回复 使用道具 举报
黑马网友  发表于 2011-7-14 22:42:04
藤椅

回复 2 # 的帖子

equals的返回值是boolean,肯定是返回给底层代码,还有HashCode的返回值int都应该是返回给底层代码的,因为在main中并没有调用equals和hashcode这两方法,也没有接收返回值的变量。有重复的,map的是覆盖更改,但set是不是也是更改,还是直接就跳过相同的。我的意思是在add时就调用那两方法,还是在遍历输出时调用。我懂你说的意思是,add元素底层就调用那两方法
回复 使用道具 举报
Collection接口有两个子接口:List接口和Set接口。
List本身就是一个变长的数组,当数组中的空间用完后,就自动开辟一个更大的数据,然后将原来的数据复制到新数组中,以此来扩展数据的长度。ArrayList就是如此。List接口中的元素可以重复。
而Set接口是不允许存在重复元素的。
先看一个例子,现在写一个Point类:[code=java]package org.cxy.collection;

public class Point {
        private int x;
        private int y;
        public Point(int x,int y){
                this.x = x;
                this.y = y;
        }
        public String toString(){
                return "坐标(x="+this.x+",y="+this.y+")"        ;
        }
}[/code]然后写一个测试类:[code=java]package org.cxy.collection;

import java.util.*;

public class HashSetDemo {
        public static void main(String[] args){
                HashSet<Point> hash = new HashSet<Point>();
                hash.add(new Point(5,4));
                hash.add(new Point(5,4));
                hash.add(new Point(1,2));
                for(Point p:hash){
                    System.out.println(p);
                }
        }
}[/code]执行结果:[code=java]坐标(x=5,y=4)
坐标(x=5,y=4)
坐标(x=1,y=2)[/code]刚才说了,Set接口是可以消除重复元素的,但是程序的执行结果,却存在了相同的元素。这是为什么?

换成String类呢?[code=java]package org.cxy.collection;

import java.util.*;

public class HashSetDemo {
        public static void main(String[] args){
                HashSet<String> hash = new HashSet<String>();
                hash.add("1");
                hash.add("1");
                hash.add("2");
                for(String str:hash){
                        System.out.println(str);
                }
        }
}[/code]程序执行的结果:[code=java]2
1[/code]结果居然没有重复元素,这又是为什么?还有,为什么先加入的是“1”,而先输出的元素确实“2”?
咱们一个个的来解决。

    如果您学习过《数据结构》这门课程,对Hash应该有点印象,对,hash表。他的特点就是高速存取,而且它是所有数据结构中存取元素速度最快的一种。而HashSet接口就是基于哈希表来设计的。
    为什么Hash表最快?数据的存储首先就是需要查找出数据将要存取的位置,顺序、折半、索引表、二叉查找树的查找效率都与查找表(待查找元素组成的集合)的长度紧密相关。都需要使用关键字不断的和查找表中的元素进行匹配。而查找的终极、必杀的做法是不去或很少进行元素的匹配。散列查找就是通过散列函数来计算元素的位置,从而尽可能的减少匹配次数,以此来提高存取速度。
    存取元素的位置,通过计算来得到。在Java中,这个计算的过程就由hashCode方法来完成。
    当将元素A、B依次插入到HashSet中,输出数据的时候,A并不一定排在B的前面,因为元素存储位置是由元素的hash码来确定的。因此咱们也说,HashSet是无序存放元素的。
    但是,凡是都有万一,万一插入数据的时候,计算出来的位置上,已经存在了元素,那又该怎么办?在《数据结构》中,存在了“冲突检测机制”,这个东西的目的,就是当目标位置存在元素的时候,就给待插入元素新找一个位置。
    而在Java中,虽然Set接口,本身要求其内的元素不能重复,但是谁来保证元素不重复呢? 答案当然就是通过咱们自己重写hashCode()和equals()方法。

因此使用HashSet<E> 的add()方法插入元素的时候:
|-  HashSet<E>会自动调用元素的hashCode()方法。
|-  然后根据hashCode()方法的返回值 来决定元素要插入的位置。
|-  如果该位置上已经存在元素了 则会调用该元素equals()方法进行比较。
|-  如果两个元素相等 则丢掉欲插入的元素。
|-  如果两个元素不相等 则新元素会被加入到另一个位置(通过冲突检测来决定哪一个位置),这样就消除了重复。
|-  范例1中使用的是Point类 其并没有重写这2个方法。因此无法消除重复。
|-  范例2中使用的是String类,在String类已经重写完了Object类的equals()和hashCode()方法,所以可以消除重复。

说白了:
|-  如果想完整的使用HashSet<E>类 那么最少要重写equals()和hashCode()方法。
|-  重写hashCode()  用于获得元素的存储位置。
|-  重写equals() 用于在两个元素的位置相同的时候 比较两个元素是否相等。

总结一下:
Set接口有两个子类:HashSet和TreeSet 。
|-  HashSet
    |-  特点:在不存在重复元素的基础上,还可以进行高速的存取元素。
     |-  要求:需要为您的类重写hashCode()和equals()方法。
|-  TreeSet
    |-  特点:在不存在重复元素的基础上,还可以将元素自动排序。
     |-  要求:需要为您的类实现Comparable接口,并重写compareTo方法。  
    |-  重写compareTo() 可以同时完成两份工作   排序和消除重复。

由于您没有问关于TreeSet的知识,所以我就不说了。
[ 本帖最后由 cxy_zy 于 2011-07-15  14:30 编辑 ]

评分

参与人数 1技术分 +2 收起 理由
技术测试刘老师 + 2 你总是能得到附加分啊,继续坚持啊!

查看全部评分

回复 使用道具 举报
黑马网友  发表于 2011-7-15 11:24:50
报纸

回复 4 # 的帖子

呵呵,这写的很详细,我要好哈看看,多谢,谢谢所有回复我的同仁!
回复 使用道具 举报
bucuo........m
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马