HashSet+HashMap的理解

1.HshSet

--首先我们来看看HashSet的一些构造方法

 /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();   //构造空的set,capacity默认初始容量为16, //load factor加载因子0.75

    }

   public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); //构造一个包含指定 collection 中的元素的新 set。
        addAll(c);
    }

    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);  //构造空的set,可以设置默认的初始容量和加载因子
    }

    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);  //构造空的set,可以设置默认的初始容量,加载因子为0.75

    }                                                                                                                    

--set集合是个无序的,而且值不可重复,理由,我用代码验证吧!

package com.zking.set;

import java.util.HashSet;
import java.util.Set;

public class set {

	public static void main(String[] args) {
		Set set = new HashSet();
		set.add(new person("a", 1));
		set.add(new person("b", 2));
		set.add(new person("c", 3));
		set.add(new person("d", 4));

		for (Object o : set) {
			System.out.println(o);
		}

	}

}

运行结果如下:


这里小白就随便写了一个利用李四替换的Hashset集合,我们可以看到我添加进去的顺序是a,b,c,d,但是我遍历出来的结果为a,d,c,b,所以说它是个无序的集合


package com.zking.set;

import java.util.HashSet;
import java.util.Set;

public class set {

	public static void main(String[] args) {
		Set set = new HashSet();
		set.add("1");
		set.add("1");
		set.add("1");
		set.add("2");
		set.add("3");

		for (Object o : set) {
			System.out.println(o);
		}

	}

}

运行结果如下:

结合代码和结果我们可得知,当Hashset集合存在相同的值时,只会显示一个值,而这个值我们也无法得知究竟是哪一个,原因是我们可以从上面的构造方法中得知信息,上面每个构造方法都会存在一个map集合,那么再结合下面这行代码就可以得出答案了,

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
  public Iterator<E> iterator() {
        return map.keySet().iterator();  //迭代map的键
    }

每次去为HashSet集合添加数据时都会调用到add()这个方法,而这个方法其实是在给map集合去赋值,而map集合是一个键值对的形式(接下来会讲解),加进去的值转化成了key,也就是键的形式,而map集合的键是不可重复的,所以说HashSet是个无序且不重复的集合。


--接下来我们来探索一下set的equals的特点

我们都知道==比较的是地址,equals是比较的值

但是在set中equals默认比较的是地址

接下来小白就用简单的代码来验证

package com.zking.set;

import java.util.HashSet;
import java.util.Set;

public class set {

	public static void main(String[] args) {
		Set set = new HashSet();
		set.add(new person("a", 1));
		set.add(new person("b", 2));
		set.add(new person("c", 3));
		set.add(new person("d", 4));

		for (Object o : set) {
			if(o.equals(new person("b", 2))) {
				continue;
			}
			System.out.println(o);
		}

	}

}

运行结果如下:

代码的意思是,我去判断集合中是否有new person("b",2)这个对象,有的话我就continue退出当前的循环,但是结果显示并不是这样,new person("b",2)这个对象依旧别遍历出来,原因是由于字符缓冲池的缘故,添加到集合的new person("b",2),和判断时候的new person("b",2)并不是同一个对象,因为重新别实例化了,所以说,这两个对象的地址也就不同,如想成功解决,可进行这样的操作

package com.zking.set;

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

	public person() {
		super();
		// TODO Auto-generated constructor stub
	}

	public person(String name, int age) {
		super();
		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;
	}

	@Override
	public String toString() {
		return "person [name=" + name + ", age=" + age + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

重写equals这个方法

//重写equals这个方法
	@Override
	public boolean equals(Object obj) {
		// 判断当前对象是不是person
		if (obj instanceof person) {
			// 强转为person类
			person p = (person) obj;
			if (this.name.equals(p.name) && this.age == p.age) {
				return true;
			}
		}

		return false;
	}

}

或者 使其变为同一个对象

package com.zking.set;

import java.util.HashSet;
import java.util.Set;

public class set {

	public static void main(String[] args) {
		Set set = new HashSet();
		set.add(new person("a", 1));
//使其变为同一个对象
		person p=new person("b", 2);
		set.add(p);
		set.add(new person("c", 3));
		set.add(new person("d", 4));

		for (Object o : set) {
			if(o.equals(new person("b", 2))) {
				continue;
			}
			System.out.println(o);
		}

	}

}

依照上面的解决方法可以很好的解决这一问题,这就是我对于HashSet的一些了解!


2.HashMap集合

--首先我们来看看HashMap的一些构造方法

static final float DEFAULT_LOAD_FACTOR = 0.75f;//加载因子
 
 public HashMap(int initialCapacity, float loadFactor)  //可以设置初始容量和加载因子

  public HashMap(int initialCapacity)//可以设置初始容量,加载因子为0.75

public HashMap()//初始容量为16,加载因子为0.75

    public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    } //k代表键,v代表值,有一个继承关系,加载因子0.75

--通常我们使用的最多的一种形式是最后一种构造方法,他是一种键值对的形式进行存储的,那么他是如何进行存储的呢?

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }  //map集合添加数据

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

小白看到这么多代码实在是头疼,但是我们来抓几个点来进行了解

首先,我们都知道,我们在为HashMap添加数据的时候会调用Put(key,value)的这个方法,而这个方法返回的是

putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)这个方法,在这个方法我们可以看到一个单词Node(节点),这个是个关键点,然后我们来看看Node这个方法

 static class Node<K,V> implements Map.Entry<K,V>

我们会看到这个方法,这个方法中有个Entry<K,V>这个接口,所以我们可以得出一个结论Entry<K,V>这个接口是一个泛型的接口,而Entry是父类型作为参数,所以说,HashMap其实是一个泛型一维数组,存储方式是K,V,一种键值对的形式存储的

这里小白画了个关系图来简略看看Arraylist和Map的结构



--接下来我们看看一些HashMap的一些特性

下面是我简单的写了一些HashMap的一个方法

package com.zking.set;

import java.util.HashMap;
import java.util.Map;


public class set {

	public static void main(String[] args) {
	Map<String ,Integer> m=new HashMap();
	m.put("a", 1);
	m.put("b", 2);
	m.put("c", 3);
	m.put("d", 4);
		
		System.out.println(m);
			
		System.out.println("HashMap的键和值");
		for (Object o : m.entrySet()) {
			System.out.println(o);
		}
		System.out.println("HashMap的键");
		for (String s : m.keySet()) {
			System.out.println(s);
		}
		
		System.out.println("HashMap的值");
		for (Integer i : m.values()) {
			System.out.println(i);
		}
		
	}

}

运行结果:



以上这些,我们都可以在源码中找的答案

一般来说,我们要获取一个对象的信息在不遍历输出的时候是要重写tostring方法的,但是HashMap源码提供了toString的方法

 public final String toString() { return key + "=" + value; }
所以对应的Key set(),entryset(),value()这些方法可以查看源码,了解原理



--好啦,小白就介绍到这里啦,虽然写的不是很好,但这只是个起步,小白会慢慢朝成为大神的方向迈进的,希望大家多多支持,这也是小白的动力哦!

猜你喜欢

转载自blog.csdn.net/mr_xiayijie/article/details/80459021
今日推荐