Java 基础学习之Set集合

一 Set 的特点

无序(没有下标)不重复.主要的分类有HashSet 和 TreeSet

1.HashSet

 特点: 

        (1)底层使用数据结构的hash算法实现的,因此具有很好的存取,查找的性能。

        (2)hashSet是线程不安全,所以它相对于线程安全的更快一些。 

        (3)hashSet值可以为null。

        (4) 不存在顺序

1.去重实例
public static void fun() {
		//创建一个HashSet 
		//保存 f f  a a   b  b  d  d
		
		HashSet<String> set =new HashSet<>();
		set.add("f");
		set.add("f");
		set.add("a");
		set.add("a");
		set.add("b");
		set.add("b");
		set.add("d");
		set.add("d");
		//有序:怎么存进去,打印出来就怎样
		//迭代器遍历
		Iterator<String> iterator = set.iterator();
		while (iterator.hasNext()) {
			String n =  iterator.next();
			System.out.println(n);
		}
		// foreach 增强for循环
		/*for (String s : set) {
			System.out.println(s);
		}*/
	}
在上面的例子中 打印结果为 
a
b
d
f

重合的数据无法被添加进集合,以上是操作系统类,如果对自定义类添加则无法去重需要重写 equals 和 hashcode

例:

先创建一个person类

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 + "]";
	}
}
public static void fun2() {
		//创建Set 集合 保存6个人 两两重复
		HashSet<Person> pSet = new HashSet<>();
		pSet.add(new Person("峰", 12));
		pSet.add(new Person("峰", 12));
		pSet.add(new Person("鹏", 18));
		pSet.add(new Person("鹏", 18));
		pSet.add(new Person("坤", 15));
		pSet.add(new Person("坤", 15));
		pSet.add(new Person("宝", 12));
		pSet.add(new Person("海", 18));
		
		for (Person person : pSet) {
			System.out.println(person);
		}
	}
此时打印结果是
Person [name=峰, age=12]
Person [name=坤, age=15]
Person [name=宝, age=12]
Person [name=海, age=18]
Person [name=鹏, age=18]
Person [name=鹏, age=18]
Person [name=坤, age=15]
Person [name=峰, age=12]

此时并没有去重,只要创建一个对象 就会给每一个对象 分配一个新的HashCode码.当添加到set中的对象 HashCode 不相同时 ,没有调用equals 方法,并且对象直接存入Set集合中.
所以要在person类中重写HashCode 和 equals 方法

	@Override
	public int hashCode() {
		// 复杂度 ,是为了减少HashCode码的重复,提高去重时调用equals方法的效率
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		//防御性判断
		if (this == obj)//如果两个地址相同.
			return true; //肯定是同一个对象
		    
		if (obj == null)//传进来的不是空(调用方法肯定不是空的)
			return false;//不是一个对象
		if (getClass() != obj.getClass())//判断两个对象是不是同一个类创建的
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

当 添加值:

        重写equals 和 hashcode equals相同时,hashcode一定相同,equals不同时,hashcode不一定不同 存在以下几种情况:

       (1)当equals 和 hashcode相同时候,set就会认为这是一个重复的对象,不会被add进去;

       (2)当equals 相同 hashcode不同的时候,就会在链表分别不同两个bocket;

         (3) 当equals 不同 hashcode相同的时候,就会在链表中的一个位置上面保存了两个对象,这样获取其中一个值性能就会降低了;

          所以在add对一个对象的添加要重写equals和hashcode,保证对象不重复性。

LinkedHashSet 

public static void fun3() {
		//Linked 有序 (怎么存的 打印出来还是那个顺序)
		LinkedHashSet<String> set  = new LinkedHashSet<>();
		set.add("f");
		set.add("f");
		set.add("a");
		set.add("a");
		set.add("b");
		set.add("b");
		set.add("d");
		set.add("d");
		for (String string : set) {
			System.out.println(string);
		}
	}
f
a
b
d

Linked 表示有序(怎么存的 打印出来还是那个顺序)

要求:利用set集合a , b, c, d 去除ArrayList集合中的重复元素(操作原ArrayList)

ArrayList 保存了  a a b b c c d d ;

HashSet<String> set = new HashSet<>();
		ArrayList<String> list = new ArrayList<>();
		list.add("a");
		list.add("a");
		list.add("b");
		list.add("b");
		list.add("c");
		list.add("c");
		list.add("d");
		list.add("d");
	     
		set.addAll(list);
		list.clear();
		list.addAll(set);
		System.out.println(list);
	}

2.TreeSet

1.

           特点:

             (1) 底层使用数据结构二叉树算法进行维护的,因此它的性能相对hashset来说,更差一些,因为它内部会自动进行排序操作。

              (2)TreeSet也是线程不安全

              (3)排序分为自然排序和定制排序,自然排序是treeset内部会对add进来的值进行自动排序,定制排序可以对排序的条件进行限制。


排序步骤:

1.实现CompareTo 接口

2.重写接口中的compareTo()方法

3.编写你想要的排序规则

public static void fun1() {
		//创建一个TreeSet 添加几个数 查看效果
		TreeSet<Integer> num = new TreeSet<>();
		num.add(1);
		num.add(8);
		num.add(2);
		num.add(5);
		num.add(9);
		num.add(4);
		num.add(4);
		num.add(4);
		for (Integer integer : num) {
			System.out.println(integer);
		}
	}
1
2
4
5
8
9

集合会调用compareTo(Object obj) 来进行大小的比较,可以对值的大小比较,也可以对对象是否相同来进行比较。如果返回0的时候是相等,非0的时候是不相等。

TreeSet 在存储的时候 只跟compareCode 返回值有关

实现 comparable 中的方法.

返回0 的时候 只有一个元素

返回 正数  打印数据 正序输出

返回 负数  打印数据 到序输出

二叉树

存储规则

比我小的数 放到我的左边(返回负数的情况)

比我大的数 放到我的右边(返回正数的情况)

返回 0 就不存储

输出规则

以升序输出(打印)

定制排序:

         TreeSet内部默认是以升序进行排序,而如果要以降序进行排序的时候可以通过comparator来进行实现,可以通过该接口下面的compare(Object o1,Object o2)来进行比较.

  实现方式:

                     可以通过comparator来进行实现,可以通过该接口下面的compare(Object o1,Object o2)来进行比较 例如:

public static void fun2() {
		// 创建一个集合TreeSet 添加4个人 (同样利用上面的person类)
		//按年龄排序
		TreeSet<Person> persons = new TreeSet<>();
		persons.add(new Person("海", 15));
		persons.add(new Person("蛋", 11));
		persons.add(new Person("斌", 10));
		persons.add(new Person("瑞", 16));
		persons.add(new Person("豆", 15));
		persons.add(new Person("剩", 14));
		for (Person person : persons) {
			System.out.println(person);
		}
	}

需要在person类中重写 int compareTo 来实现

public int compareTo(Person o) {
	/*	//按年龄
		int num = this.age - o.getAge();
		return num;*/
		//按名字
//		return this.name.compareTo(o.getName());
		
		//注要按年龄排 次要按姓名
		
		/*if (this.age!=o.getAge()) {
			return this.age - o.getAge();
		}else {
			return this.name.compareTo(o.getName());
		}*/
		
		int num = this.age - o.getAge();
		return num!=0?this.age - o.getAge():this.name.compareTo(o.getName());
	}

对于系统类的排序,由于系统类本身具有内置的排序规则,所以如果要自定义排序规则,需要创建比较器重写比较规则.

例:

public static void fun3() {
		//集合中保存字符串 按字符串长度排序
		//利用TreeSet 的构造方法 直接将比较器的实现类传进去.
		TreeSet<String> set = new TreeSet<>(new compareImpl());
		set.add("asfgddfb");
		set.add("asfgddfb");
		set.add("as");
		set.add("asfb");
		set.add("asfgdfb");
		set.add("addfbfghjgfmhyku");
		
	System.out.println(set);
	}

比较器:

class compareImpl implements Comparator<String>{
// 实现比较器方法
	@Override
	public int compare(String o1, String o2) {
		int length = o1.length()-o2.length();
		int num = o1.compareTo(o2);
		int rel = length==0?num:length;
		return rel==0?-1:rel;
	}
}
打印结果为:
[as, asfb, asfgdfb, asfgddfb, asfgddfb, addfbfghjgfmhyku]
要保证数据完整,一般但返回值为零时,建议返回一个不为零的值.








猜你喜欢

转载自blog.csdn.net/vlin_fan/article/details/80410214
今日推荐