Java Set 接口

Set 接口

Set 接口的定义

Set 是 Collection 的子接口 ,Set 接口以散列的形式存储数据,所以元素没有顺序。可以存储一组无序且唯一的对象。在实际开发中也不能直接实例化 Set,需要对其实现类进行实例化再完成业务操作。Set 的常用实现类主要有:

  • HashSet
  • LinkedHashSet
  • TreeSet

Set 接口的实现类

HashSet

HashSet 是开发中经常使用到的实现类,存储一组无序且唯一的对象。这里的无序是指元素的存储顺序和遍历顺序不一致。

使用方法非常简单,让我们一起实践一下:

public class TestHashSet {
	public static void main(String[] args) {
		HashSet hashSet = new HashSet();
		hashSet.add("Hello");
		hashSet.add("World");
		hashSet.add("Java");
		hashSet.add("Hello");
		System.out.println("hashSet的长度:"+hashSet.size());
		System.out.println("遍历 hashSet");
		Iterator iterator = hashSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
		System.out.println();
		hashSet.remove("Hello");
		System.out.println("删除之后再次遍历 hashSet");
		//这里 hashSet 对象改变了,所以我们需要重新给 iterator 赋值
		iterator = hashSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
	}
}

运行结果:

在这里插入图片描述

对集合进行了添加两个 “Hello” 的操作,但是只保存了一个,这是因为 HashSet 集合的元素是唯一的

LinkedHashSet

LinkedHashSet 是 Set 的另一个接口,可以存储一组有序且唯一的元素。这里的有序是指元素的存储顺序和遍历顺序是一致的。

示例:

public class TestLinkedHashSet {
	public static void main(String[] args) {
		LinkedHashSet linkedHashSet = new LinkedHashSet();
		linkedHashSet.add("Hello");
		linkedHashSet.add("World");
		linkedHashSet.add("Java");
		linkedHashSet.add("Hello");
		System.out.println("linkedHashSet 的长度:"+linkedHashSet.size());
		System.out.println("编译 linkedHashSet");
		Iterator iterator = linkedHashSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
		System.out.println();
		linkedHashSet.remove("World");
		System.out.println("删除之后遍历 linkedHashSet");
		iterator = linkedHashSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
	}
}

运行结果:

在这里插入图片描述

我们对集合执行了添加两个 “Hello” 的操作,但是只保存了一个,这是因为LinkedHashSet 集合的元素是唯一的,既不能出现两个相等的元素。字符串如此,其他对象也是一样。我们定义一个 A 类,将类的实例化对象存入集合

public class Test{
    public static void main(String[] args){
        LinkedHashSe set = new LinkedHashSet();
        set.add(new A(1));
        set.add(new A(1));
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.print(iterator.next()+",");
        }
    }
}

class A{
    private int num;
    public A(int num){
        this.num = num;
    }
    @Override
    public String toString(){
        return "A [num=" + num +"]";
    }
}

运行结果:

A [num=1],A [num=1],

我们可以看到两个 A 对象都保存到了集合中,也就是说当前集合不认为这两个对象相等,那么程序是如何来鉴别两个对象是否相等呢?通过继承自 Object 类的 equals() 方法来判断,Object 类的 equals() 方法定义如下

public boolean equals(Object obj){
    return (this == obj);
}

"==" 表示比较两个对象的内存地址,所以虽然两个 A 对象的 num 值相等,也就是从内容的角度来看是相等的,但是内存地址不同,所有程序 会认为是不想等的两个对象

  • 首先让我们了解一下 LinkedHashSet 判断两个对象是否相等的原理

首先会判断两个对象的 hashCode 是否相等,什么是hashCode?简单来说就是将对象的内部信息(如内存地址,属性值等),通过某种特定规则转换成一个散列值,也就是该对象的 hashCode。两个不同对象的 hashCode 可能相等,但是 hashCode 不相等的两个对象一定不是同一个对象

所以集合在判断两个对象是否相等时,会先比较它们的 hashCode,如果不相等,则认为不是同一个对象,可以添加。如果 hashCode 相等,还不能认为两个对象就是相等的,需要通过 equlas() 方法进一步判断。如果 equals() 方法为 true,则不会重复添加;如果 equals() 方法为 false,则正常添加。

先判断 hashCode 是否相等可以减少 equals() 方法的调用,提高效率。所以两个 A 相等的前提是 hashCode 相等,且 equlas() 方法返回 true

对类 A 进行修改:

class A{
    .....
    @Override
    public boolean equals(Object obj){
        return true;
    }
    @Override
    pubilc int hashCode(){
        return 1;
    }
}

运行结果:

A [num=1],

程序显示只存储了一个 A 对象。

TreeSet

在 Set 的接口中,除了 LinkedHashSet 可以存放有序元素之外,TreeSet 中保存的元素也是有序的,并且 TreeSet 的有序和 LinkedHashSet 的有序有所不同。LinkedHashSet 的有序是指元素的存储顺序和遍历顺序是一致的,即元素按什么顺序进去,遍历时就按什么顺序输出。TreeSet 的有序是指集合内部会自动给所有的元素按照升序进行排序,即无论存入元素的顺序是什么,遍历时会按照升序进行输出。

public class TestTreeSet {
	public static void main(String[] args) {
		TreeSet treeSet = new TreeSet();
		treeSet.add(1);
		treeSet.add(2);
		treeSet.add(5);
		treeSet.add(3);
		treeSet.add(1);
		treeSet.add(4);
		System.out.println("treeSet 的长度:"+treeSet.size());
		System.out.println("遍历 treeSet");
		Iterator iterator = treeSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
		System.out.println();
		treeSet.remove(3);
		System.out.println("删除之后遍历 treeSet");
		iterator = treeSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
	}
}

运行结果:

在这里插入图片描述

因为 TreeSet 内部会自动按照升序对元素进行排序,所以添加到 TreeSet 集合中的元素必须具备排序的功能,现在我们创建一个 A 类,同时将 A 的实例化对象保存到 TreeSet 中

class A{
	private int num;
	public A(int num) {
		this.num = num;
	}
	
	@Override
	public String toString() {
		return "A [num="+num+"]";
	}
}

public class TestTreeSetA {
	public static void main(String[] args) {
		TreeSet treeSet = new TreeSet();
		treeSet.add(new A(2));
		treeSet.add(new A(1));
		treeSet.add(new A(5));
		treeSet.add(new A(1));
		treeSet.add(new A(4));
		treeSet.add(new A(3));
		System.out.println("treeSet的长度"+treeSet.size());
		System.out.println("遍历 treeSet");
		Iterator iterator = treeSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
		System.out.println();
		treeSet.remove(new A(3));
		System.out.println("删除之后遍历 treeSet");
		iterator = treeSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
	}
}

尝试运行会报错,报错原因是 A 不具备排序功能,如何解决呢?让 A 实现 Comparable 接口即可。

class A implements Comparable{
	private int num;
	public A(int num) {
		this.num = num;
	}
	
	@Override
	public int compareTo(Object o) {
		/**
		 * A.compareTo(B)
		 * 返回值
		 * 1 表示 A 大于 B
		 * 0 表示 A 等于 B
		 * -1 表示 A 小于 B
		 * 
		 * */
		A a = (A) o;
		if(this.num > a.num) {
			return 1;
		}else if(this.num == a.num) {
			return 0;
		}else {
			return -1;
		}
	}
	@Override
	public String toString() {
		return "A [num="+num+"]";
	}
}

public class TestTreeSetA {
	public static void main(String[] args) {
		TreeSet treeSet = new TreeSet();
		treeSet.add(new A(2));
		treeSet.add(new A(1));
		treeSet.add(new A(5));
		treeSet.add(new A(1));
		treeSet.add(new A(4));
		treeSet.add(new A(3));
		System.out.println("treeSet的长度"+treeSet.size());
		System.out.println("遍历 treeSet");
		Iterator iterator = treeSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
		System.out.println();
		treeSet.remove(new A(3));
		System.out.println("删除之后遍历 treeSet");
		iterator = treeSet.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+", ");
		}
	}
}

运行结果:

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值