简述HashSet的remove方法

HashSet的remove方法

  • 准备主要代码
  • HashSet的remove方法的执行过程
  • 添加并删除自定义对象

准备主要代码

  1. 创建HashSet对象
//		创建一个hashSet集合对象
		HashSet<Object> hashSet = new HashSet<Object>();

查看HashSet的构造方法

//可以产出这里创建了HashMap<>()
public HashSet() {
        map = new HashMap<>();
    }
  1. 向HashSet中添加元素
//		添加String类型元素
		hashSet.add("a");
		hashSet.add("b");
		hashSet.add("c");

查看hashSet中的元素

//		查看源码,在AbstractCollection类中重写了toString方法
		System.out.println(hashSet);

在AbstractCollection类中重写的toString方法

    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

结果

[a, b, c]
  1. 执行remove方法
//		删除集合中的元素
		hashSet.remove("b");
		System.out.println(hashSet);

结果

[a, c]

HashSet的remove方法的执行过程

  1. HashSet类中的remove方法
public boolean remove(Object o) {
//		这里调用了map对象(HashMap类)的remove方法
        return map.remove(o)==PRESENT;
    }
  1. HashMap类中的remove方法
  public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }
  1. 这里调用了hash方法和removeNode方法
  • hash方法
static final int hash(Object key) {
        int h;
//		如果对象不为null怎返回传入对象调用hashCode的返回值
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  • HashMap的removeNode方法
final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        //1.因为前面调用了HashSet的add方法add方法中有调用了HashMap中的putVal方法
        //在putVal方法中对table已经初始化过了默认为16
        //因为传入的是字符串"b",他们调用hashCode方法的范围值和之前的添加的"b"是相同的
        //所以这里可以得到之前添加的元素对应的索引
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            Node<K,V> node = null, e; K k; V v;
            if (p.hash == hash &&
            //	2.这里传入的字符串"b"的hash值和之前存入的"b"的hash值相同
            //	传入对象和目标对象地址相同,所以这里把p的地址值赋值给node
            // 	*但是如果出入对象和要删除的对象地址值不同,则需要调用传入对象的equals方法
            //	如果返回的是true则把p的地址值赋给node
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            //3.由于node不为null,上面传入的matchValue为false所以这里进入if语句
            //返回node,则remove方法返回node,在add方法中可以知道node的value是之前添加元素的value
            //所以返回node它的value就是传入对象的PRESENT,比较之后返回true删除成功
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                //node声明的时候属于Node类,但是Node和TreeNode没有子父类关系
                //所以进入else if语句
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                //又因为上面把p赋给了node所以这里node==p返回true
                else if (node == p)
                	//把node后面的元素赋给当前数组索引的位置
                	//node对象中并没有给next赋值
                	//所以这里相当于把数据当前索引的位置 置为空,删除成功
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

添加并删除自定义对象

  • 从上面的源码可以看出如果想要删除自定义对象需要重写该类的hashCode方法和eqals方法

  • 创建一个Person类

public class Person {
	private String id;
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	@Override
	public int hashCode() {
		return id.hashCode();
	}
	@Override
	public boolean equals(Object obj) {
	//	对传入的对象做判断是否属于Person类
	//	否则有可能出现类型转换异常
		if(obj instanceof Person) {
			Person person = (Person)obj;
			return id.equals(person.getId());
		}
		return false;
	}
	//重写toString方法方便观察对象的属性
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + "]";
	}
}
  • 测试的代码
	public static void main(String[] args) {
//		创建一个hashSet集合对象
		HashSet<Object> hashSet = new HashSet<Object>();
//		创建三个Person类对象
		Person person1 = new Person();
		Person person2 = new Person();
		Person person3 = new Person();
//		给每个对象设置一个id的属性
		person1.setId("1");
		person2.setId("2");
		person3.setId("3");
//		把对象添加进去		
		hashSet.add(person1);
		hashSet.add(person2);
		hashSet.add(person3);
		System.out.println(hashSet);
//		创建一个新的对象并把id设为"3"
		Person person = new Person();
		person.setId("3");
		hashSet.remove(person);
		System.out.println(hashSet);
	}

结果

[Person [id=1, name=null], Person [id=2, name=null], Person [id=3, name=null]]
[Person [id=1, name=null], Person [id=2, name=null]]
  • 可以看出这里删除成功了,所以如果想要以特定的条件删除某个对象需要重写该对象所属类的hashCode方法和equals方法。根据源码也可以看出来在调用equals方法之前会使用"=="进行比较,如果返回false则再调用equals方法进行比较。
  • String类比较特殊,因为如果直接使用赋值的方法创建String对象,如果第一次创建就在常量池中存储该字符串对象,后来创建的字符串如果和常量池中的字符串相同,则直接把常量池中的地址赋值给新的对象,则前后两次创建的对象指向同一个地址,该地址在常量池中并且存储了对应的String对象。
  • 对于自定义的对象因为每此时使用关键字new创建一个对象的时候是在堆中创建的,他们的地址值不同,所以需要调用后面的equals方法进行比较。

你可能感兴趣的:(Java基础)