hashset 订阅
HashSet类,是存在于java.util包中的类 [1]  。同时也被称为集合,该容器中只能存储不重复的对象。 展开全文
HashSet类,是存在于java.util包中的类 [1]  。同时也被称为集合,该容器中只能存储不重复的对象。
信息
别    称
集合
特    点
存储不重复的对象
存在于
java.util.HashSet [1]
所属语言
java
基    础
HashMap
外文名
HashSet
HashSetHashSet 概述
对于 HashSet 而言,它是基于 HashMap 实现的,底层采用 HashMap 来保存元素,所以如果对 HashMap 比较熟悉了,那么学习 HashSet 也是很轻松的 [2]  。我们先通过 HashSet 最简单的构造函数和几个成员变量来看一下,证明咱们上边说的,其底层是 HashMap:
收起全文
精华内容
下载资源
问答
  • HashSet

    2016-04-07 16:15:15
    hashSet

    1.在对hashSet进行讲解之前,我们先来说一下equals()和hashCode。
    (1)首先是equals(),默认情况下equals()是比较两个对象的引用是否相同

    public class Fish {
        private int weight;
        private String color;
    
    
    
        public Fish(int weight, String color) {
            this.color = color;
            this.weight = weight;
        }
    
        public int getWeight() {
            return weight;
        }
    
        public void setWeight(int weight) {
            this.weight = weight;
        }
    
    
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
    }
    
    public class EqualsTest {
        public static void main(String[] args) {
            Fish f1 = new Fish(1, "blue");
            Fish f2 = new Fish(1, "blue");
    
    
    
            System.out.println(f1 == f2);
            System.out.println(f1.equals(f2));
        }
    }

    ——————运行结果为——————

    false
    false

    很明显,Fish为两个对象所以用equals比较时返回false
    (2)重新equals() 方法后

    public class Fish {
        private int weight;
        private String color;
    
    
    
        public Fish(int weight, String color) {
            this.color = color;
            this.weight = weight;
        }
    
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((color == null) ? 0 : color.hashCode());
            result = prime * result + weight;
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Fish other = (Fish) obj;
            if (color == null) {
                if (other.color != null)
                    return false;
            } else if (!color.equals(other.color))
                return false;
            if (weight != other.weight)
                return false;
            return true;
        }
        public int getWeight() {
            return weight;
        }
    
        public void setWeight(int weight) {
            this.weight = weight;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
    }
    
    public class EqualsTest {
        public static void main(String[] args) {
            Fish f1 = new Fish(1, "blue");
            Fish f2 = new Fish(1, "blue");
    
    
    
            System.out.println(f1 == f2);
            System.out.println(f1.equals(f2));
        }
    }

    ——————运行结果为——————

    false
    true

    此例子中我复写了equals()方法和hashcode()方法,使得equals()方法脱离的本意,不再是比较两个对象的引用是否相同,而是比较其内容是否相同。

    一般来讲,equals这个方法是给用户调用的,如果你想判断2个对象是否相等,你可以重写equals方法,然后在代码中调用,就可以判断他们是否相等了。简单来讲,equals方法主要是用来判断从表面上看或者从内容上看,2个对象是不是相等。举个例子,有个学生类,属性只有姓名和性别,那么我们可以认为只要姓名和性别相等,那么就说这2个对象是相等的。
    hashcode方法一般用户不会去调用,比如在hashmap中,由于key是不可以重复的,他在判断key是不是重复的时候就判断了hashcode这个方法,而且也用到了equals方法。这里不可以重复是说equals和hashcode只要有一个不等就可以了!所以简单来讲,hashcode相当于是一个对象的编码,就好像文件中的md5,他和equals不同就在于他返回的是int型的,比较起来不直观。我们一般在覆盖equals的同时也要覆盖hashcode,让他们的逻辑一致。举个例子,还是刚刚的例子,如果姓名和性别相等就算2个对象相等的话,那么hashcode的方法也要返回姓名的hashcode值加上性别的hashcode值,这样从逻辑上,他们就一致了。

    要从物理上判断2个对象是否相等,用==就可以了。

    HashSet 讲解
    对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码:

    
      public class HashSet<E> 
    extends AbstractSet<E> 
    implements Set<E>, Cloneable, java.io.Serializable 
     { 
    // 使用 HashMap 的 key 保存 HashSet 中所有元素
    private transient HashMap<E,Object> map; 
    // 定义一个虚拟的 Object 对象作为 HashMap 的 value 
    private static final Object PRESENT = new Object(); 
    ... 
    // 初始化 HashSet,底层会初始化一个 HashMap 
    public HashSet() 
    { 
    map = new HashMap<E,Object>(); 
    } 
    // 以指定的 initialCapacity、loadFactor 创建 HashSet 
    // 其实就是以相应的参数创建 HashMap 
    public HashSet(int initialCapacity, float loadFactor) 
    { 
    map = new HashMap<E,Object>(initialCapacity, loadFactor); 
    } 
    public HashSet(int initialCapacity) 
    { 
    map = new HashMap<E,Object>(initialCapacity); 
    } 
    HashSet(int initialCapacity, float loadFactor, boolean dummy) 
    { 
    map = new LinkedHashMap<E,Object>(initialCapacity 
    , loadFactor); 
    } 
    // 调用 map 的 keySet 来返回所有的 key 
    public Iterator<E> iterator() 
    { 
    return map.keySet().iterator(); 
    } 
    // 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数
    public int size() 
    { 
    return map.size(); 
    } 
    // 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,
    // 当 HashMap 为空时,对应的 HashSet 也为空
    public boolean isEmpty() 
    { 
    return map.isEmpty(); 
    } 
    // 调用 HashMap 的 containsKey 判断是否包含指定 key 
    //HashSet 的所有元素就是通过 HashMap 的 key 来保存的
    public boolean contains(Object o) 
    { 
    return map.containsKey(o); 
    } 
    // 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap 
    public boolean add(E e) 
    { 
    return map.put(e, PRESENT) == null; 
    } 
    // 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素
    public boolean remove(Object o) 
    { 
    return map.remove(o)==PRESENT; 
    } 
    // 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素
    public void clear() 
    { 
    map.clear(); 
    } 
    ... 
     }
    
    由上面源程序可以看出,HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。 
    
    HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。 
    掌握上面理论知识之后,接下来看一个示例程序,测试一下自己是否真正掌握了 HashMap 和 HashSet 集合的功能。 
     class Name
    {
        private String first; 
        private String last; 
    
        public Name(String first, String last) 
        { 
            this.first = first; 
            this.last = last; 
        } 
    
    
        public boolean equals(Object o) 
        { 
            if (this == o) 
            { 
                return true; 
            } 
    
    if (o.getClass() == Name.class) 
            { 
                Name n = (Name)o; 
                return n.first.equals(first) 
                    && n.last.equals(last); 
            } 
            return false; 
        } 
    }
    
    
    public class HashSetTest
    {
        public static void main(String[] args)
        { 
            Set<Name> s = new HashSet<Name>();
            s.add(new Name("abc", "123"));
            System.out.println(
                s.contains(new Name("abc", "123")));
        }
    } 

    上面程序中向 HashSet 里添加了一个 new Name(“abc”, “123”) 对象之后,立即通过程序判断该 HashSet 是否包含一个 new Name(“abc”, “123”) 对象。粗看上去,很容易以为该程序会输出 true。

    实际运行上面程序将看到程序输出 false,这是因为 HashSet 判断两个对象相等的标准除了要求通过 equals() 方法比较返回 true 之外,还要求两个对象的 hashCode() 返回值相等。而上面程序没有重写 Name 类的 hashCode() 方法,两个 Name 对象的 hashCode() 返回值并不相同,因此 HashSet 会把它们当成 2 个对象处理,因此程序返回 false。

    由此可见,当我们试图把某个类的对象当成 HashMap 的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的 equals(Object obj) 方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
    如下程序就正确重写了 Name 类的 hashCode() 和 equals() 方法,程序如下:

    class Name 
    { 
        private String first;
        private String last;
        public Name(String first, String last)
        { 
            this.first = first; 
            this.last = last; 
        } 
        // 根据 first 判断两个 Name 是否相等
        public boolean equals(Object o) 
        { 
            if (this == o) 
            { 
                return true; 
            } 
            if (o.getClass() == Name.class) 
            { 
                Name n = (Name)o; 
                return n.first.equals(first); 
            } 
            return false; 
        } 
    
        // 根据 first 计算 Name 对象的 hashCode() 返回值
        public int hashCode() 
        { 
            return first.hashCode(); 
        }
    
    
        public String toString() 
        { 
            return "Name[first=" + first + ", last=" + last + "]"; 
        } 
     } 
    
     public class HashSetTest2 
     { 
        public static void main(String[] args) 
        { 
            HashSet<Name> set = new HashSet<Name>(); 
            set.add(new Name("abc" , "123")); 
            set.add(new Name("abc" , "456")); 
            System.out.println(set); 
        } 
    }

    上面程序中提供了一个 Name 类,该 Name 类重写了 equals() 和 toString() 两个方法,这两个方法都是根据 Name 类的 first 实例变量来判断的,当两个 Name 对象的 first 实例变量相等时,这两个 Name 对象的 hashCode() 返回值也相同,通过 equals() 比较也会返回 true。

    程序主方法先将第一个 Name 对象添加到 HashSet 中,该 Name 对象的 first 实例变量值为”abc”,接着程序再次试图将一个 first 为”abc”的 Name 对象添加到 HashSet 中,很明显,此时没法将新的 Name 对象添加到该 HashSet 中,因为此处试图添加的 Name 对象的 first 也是” abc”,HashSet 会判断此处新增的 Name 对象与原有的 Name 对象相同,因此无法添加进入,程序在①号代码处输出 set 集合时将看到该集合里只包含一个 Name 对象,就是第一个、last 为”123”的 Name 对象。

    展开全文
  • hashset

    2017-06-02 22:20:05
    hashset import java.util.HashSet; import java.util.Set; public class hashset_test { public static void main(String[] args) { Set s1=new HashSet(); s1.add("aaa"); s1.add("aaa"); s1.add("bb

    hashset

    import java.util.HashSet;
    import java.util.Set;
    
    public class hashset_test {
    	public static void main(String[] args) {
    		Set s1=new HashSet<>();
    		s1.add("aaa");
    		s1.add("aaa");
    		s1.add("bbb");
    		System.out.println(s1.size());
    	}
    
    }
    输出结果为2


    HashSet是实现Set接口的一个类,具有以下的特点:不能保证元素的排列顺序,顺序有可能发生变化,元素不允许重复
    HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束 
    HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例
    HashSet不能添加重复的元素,当调用add(Object)方法时候,首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。

    以下转载自:http://www.hijava.org/2010/02/how-to-judge-object-repeated-for-hashset/

    查看了JDK源码,发现HashSet竟然是借助HashMap来实现的,利用HashMap中Key的唯一性,来保证HashSet中不出现重复值。具体参见代码:

    public class HashSet<E>
        extends AbstractSet<E>
        implements Set<E>, Cloneable, java.io.Serializable
    {
        private transient HashMap<E,Object> map;
    
        // Dummy value to associate with an Object in the backing Map
        private static final Object PRESENT = new Object();
    
        public HashSet() {
        map = new HashMap<E,Object>();
        }
    
        public boolean contains(Object o) {
        return map.containsKey(o);
        }
    
        public boolean add(E e) {
        return map.put(e, PRESENT)==null;
        }

    由此可见,HashSet中的元素实际上是作为HashMap中的Key存放在HashMap中的。下面是HashMap类中的put方法:
    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
            }
        }
    }

    从这段代码中可以看出,HashMap中的Key是根据对象的hashCode() 和 euqals()来判断是否唯一的。

    结论:为了保证HashSet中的对象不会出现重复值,在被存放元素的类中必须要重写hashCode()和equals()这两个方法。




    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,584
精华内容 14,233
关键字:

hashset