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()这些方法可以查看源码,了解原理
--好啦,小白就介绍到这里啦,虽然写的不是很好,但这只是个起步,小白会慢慢朝成为大神的方向迈进的,希望大家多多支持,这也是小白的动力哦!