Set 集合复习

                                                                                                                 Set 集合复习

说明: set集合在某种程度上相当于Collection ,但是Set不允许有重复的元素值,如果两个相同的元素试图添加入集合中则添加操作失败,add()方法返回false.

      Set判断两个对象是否相同不是使用==,而是根据equals方法,也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象,相反,只要两个对象用equals方法比较返回false,Set就会接受这两个对象。(在极端的情况下,即使equals返回false也有两个对象是一个对象的情况发生,这时Set就会接受这两个对象)

下面看一下三个主要的实现类

  HashSet类

实例1:

/**
 * Set接口方法演示
 * @author fcs
 *
 */
public class HashSetDemo {
   public static void main(String[] args) {
	  Set<String>  set = new HashSet<String>();
	  Set<String>  set1 = new HashSet();    //这种写法并没有错,因为HashSet也支持了泛型,但是很少有人这样写,
	  set1.add("set1");
	
	  set.add("element");
	  
	  System.out.println("set1 == null ? "+set1.isEmpty());   //判断一个set集合是否为空.
	  Set<String> sonSet = new HashSet<String>();
	  sonSet.add("son1");
	  Set<String> sonSet2 = new HashSet<String>(); 
	  sonSet2.add("son2");
	  sonSet2.add("son3");
	  
	  set.addAll(sonSet);               //将集合sonSet加入set中,相当于两个集合的并集(相当于集合之间的并集操作 )
	  boolean b = set.add("son1");      //此时再将son1加入set集合中,则与原先存在于集合中的元素(element,son1)中的一个元素相等而冲突,因此该方法
	  									//返回false.在下面的迭代中只会出现一个son1.
	  
	  System.out.println("b = "+b);
	  System.out.println("set1 == set ? "+(set1 == set)+"\n");  //创建了两个set对象
	  set.contains("son1");             //是否包含泛型元素,返回true/false;
	  set.containsAll(sonSet) ;         //是否包含另一个Collection的所有元素,返回true/false。
	  Iterator<String> it =  set.iterator();
	  while(it.hasNext()){
		System.out.println(" ---- >"+it.next());
	  }
	  
	  boolean b1 = set.remove("element");             //移除指定元素,如果set中存在指定元素,将其移除后返回true,否则返回false
	  System.out.println("b1 = "+b1);
	  
	  boolean b2 = set.removeAll(sonSet2) ;           //将集合sonSet2中元素与set元素相同的元素去掉,如果set在进行交集操作的时候发生更改了,则返回false,否则返回true(相当于集合之间的交集操作 )
	  System.out.println("b2 = "+b2);
	  System.out.println(")))0"+set.contains("son1"));
	  System.out.println("---"+sonSet2.contains("son1"));
	  sonSet2.add("son1");
	  boolean b3 = set.retainAll(sonSet2) ;           //将集合sonSet2中元素与set元素相同的保留,不相同的则去除,如果set在进行差集操作的时候发生更改了,则返回false,否则返回true(相当于集合之间的差集操作)
	  System.out.println("b3 = "+b3);
	  System.out.println("size = "+set.size());       //返回set集合中的元素个数
	  
	  set.add("1234");
	  set.add("array");
	  
	  Object   setArray  = set.toArray();   		  //这种方式也可以编译通过,但是是Object类型了,因此没有任何意义
	  Object[] setArray1 = set.toArray();   
	  System.out.println(setArray1 == setArray);
	  System.out.println("length = "+setArray1.length);
   }
}

ashSet源码解析

API文档介绍:

该类实现了Set接口,继承了AbstractSet,另外实现了Cloneable,Seriralizable接口

内部由HashMap支持,不保证迭代的顺序不变。

注意该实现不是同步的,如果多线程对该set进行操作的话会引发异常。

内部数据结构是哈希表,hashSet不允许重复,但是可以允许使用null元素。

 

hashSet的构造函数

1.构造一个空的hashSet。
public HashSet() { map = new HashMap<>(); }
2.根据给定集合构造hashSet,在构造函数中指定装载因子和容量
 public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
3. 给定装载因子和初始化容量
 public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
4.给定容量
public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
}
5. 私有构造方法,内部使用LinkedHashMap
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    } 

注意这里有一个公式:

(e==null ? e2==null : e.equals(e2))

假设e不是该set的元素,而e2set中的一个元素

(e==null ? e2==null : e.equals(e2))

首先当e是null的时候 返回  e2==null,

如果e2不等于null,则结果返回false,说明该set之前没有null,可以加入e是null的这个值

如果e2等于null,则结果返回 true,说明该set之前已经存在null值了,不能插入该null值了

 

当e不是null的时候,返回e.equals(e2)

如果e.equals(e2)是真,则结果返回true,则说明该set中已经存在与e相同的值了,不能插入(修改)该值了

如果e.equals(e2)是假,则结果返回false,则说明该set中没有与e相同的值,则可以插入(修改)该值。

    这也是set集合判断元素是否唯一的重要依据。

6.hashSet集合判断的标准验证:
public class SetEquals {
     public static void main(String[] args) {
    	 Set<Object> set  =new HashSet<Object>();
    	 set.add(new A());
    	 set.add(new A());
    	 set.add(new B());
    	 set.add(new B());
    	 set.add(new C());
    	 set.add(new C());
    	 System.out.println(set);
	}
     
}

//只重写equals方法
class A{
	public boolean equals(Object obj){
		return true;
	}
}
//只重写hashCode方法
class B{
	public int hashCode(){
		return 1;
	}
}

//都重写了
class C{
	public int hashCode(){
		return 2;
	}
	public boolean equals(Object obj){
		return true;
	}
}

输出结果:[cn.com.set.B@1, cn.com.set.B@1, cn.com.set.C@2,cn.com.set.A@5f5660ef, cn.com.set.A@1ff61bcf]

说明只有重写了hashcode和equals方法判断重复才有效

这里给出重写hashCode方法的规则:

1.在程序运行中,同一个对象多次调用hashCode 方法应该返回相同的值,

2.当两个对象通过equals方法比较返回true的时候,这两个对象的hashCode()方法返回相等的值,

3.对象中用作equals方法比较标准的Field,都应该用来计算hashCode值

总结

哈希表判断元素是否相同:

1.判断的是两个元素的哈希值是否相同,如果相同,再判断两个对象的内容是否相同

2.判断哈希值相同,HashCode方法(返回此字符串的哈希码),判断内容相同,equals方法

注:哈希值不同,就不需要第二次判断内容是否相同

当向hashset中添加可变对象的时候,即使hashCode和equals方法返回值都相同,也不一定能保证hashset中有相同的值。

 

package cn.com.set;

import java.util.HashSet;
class R{
	int count;
	public R(int c ){
		this.count = c;
	}
	public boolean equals(Object obj){
		if(this==obj){
			return true;
		}
		if(obj != null && obj.getClass() == R.class){
			R r = (R)obj;
			if(r.count == this.count){
				return true;
			}
		}
		return false;
	}
	//根据count计算hashcode值
	public int hashCode(){
		return this.count;
	}
}
public class SetEquals2 {
    public static void main(String[] args) {
		HashSet has = new HashSet();
		has.add(new R(5));
		has.add(new R(-3));
		has.add(new R(9));
		has.add(new R(-2));
		System.out.println(has);
		java.util.Iterator  it = has.iterator();
		R first= (R)it.next();
		first.count = -3;  //这里把5改成-3
		System.out.println(has);  //出现重复
	    has.remove(new R(-3));将原来的那个-3删除
	    
	    //false
	    System.out.println("has是否包含count为-3的R对象?"+has.contains(new R(-3))); 
	    //false
	    System.out.println("has是否包含count为5的R对象?"+has.contains(new R(5))); 
    }
}

当需要向hashSet中添加可变对象时,应该更加小心

LinkedHashSet集合使用:

linkedHashSet也是根据hashCode 方法决定存储位置。同时使用链表维护元素的次序,这样可以让元素看起来是以插入的顺序保存的。linkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet。该类的实现同样不是线程同步的,也允许为null,不允许重复。

API说明:具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序。

此类提供所有可选的 Set 操作,并且允许 null 元素。与 HashSet 一样,它可以为基本操作(add、contains 和 remove)提供稳定的性能,假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比HashSet 稍逊一筹,不过,这一点例外:LinkedHashSet 迭代所需时间与 set 的大小 成正比,而与容量无关。HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量成正比。

链接的哈希 set 有两个影响其性能的参数:初始容量加载因子。它们与 HashSet 中的定义极其相同。注意,为初始容量选择非常高的值对此类的影响比对 HashSet要小,因为此类的迭代时间不受容量的影响

实例(按插入顺序输出元素值,构造方法说明):

import java.util.Collection;
import java.util.LinkedHashSet;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
    	
    	/*
    	 * 构造一个带默认初始容量 (16) 和加载因子 (0.75) 的新空链接哈希 set。
    	 */
		LinkedHashSet lhs = new LinkedHashSet();  
		
		/*
		 * 构造一个指定容量为100,默认加载因子为0.75的链接哈希set
		 */
		LinkedHashSet lhs1 = new LinkedHashSet(100);
		
		Collection coll = new ArrayList();
		coll.add("2839248");
		
		/*
		 * 根据给定的集合构造新的链接哈希set
		 */
		LinkedHashSet lhs2 = new LinkedHashSet(coll);  
		
		/*
		 * 同时指定初始容量和加载因子的构造方法
		 */
		LinkedHashSet lhs3 = new LinkedHashSet(100,0.80f);  
		
		lhs.add("string1");
		lhs.add("string2");
		lhs.add("string3");
		lhs.add("string4");
		
		lhs.remove("string3");
		System.out.println(lhs);
		lhs.add("string5");
		System.out.println(lhs);
		
	}
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值