Java基础系列六之Set集合

Set集合和List集合的区别?
  Set集合:不允许元素重复,唯一的(元素可以为null) ,不能保证迭代的顺序恒久不变(底层哈希表和hascode)
  无序(存储和取出不一致)
  List:允许元素重复,并且存储特点:有序性(存储和取出一致)
 

 通过Set集合存储字符串并遍历

代码示例

package test;

import java.util.HashSet;
import java.util.Set;

public class TestDemo4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Set<String> set = new HashSet<String>();
		set.add("hello");
		set.add("world");
		set.add("java");
		set.add("hello");
		set.add("world");
		set.add("java");
		for (String string : set) {
			System.out.println(string);
		}
	}

}
发现Set集合存储元素的时候,可以保证元素的唯一性,原因什么? 
  看源码:
  @author Administrator
  HashSet集合的add方法底层依赖于双列集合HashMap,它依赖于两个方法,HashCode()方法和equals()方法
   先比较字符串的hashCode码值,看hashCode码值是否一样,再使用equals()方法
  如果hasCode码值一样,还要比较内容是否相同,由于String本身重写了equals方法,所以不需要再重写了!

 用Set集合存储自定义对象并遍set集合存储自定义对象并遍历
 现在是自定义的类,HashSet集合的add()方法本身依赖于hashCode()和equals()方法

 在Student类中并没重写这两个方法,解决,重写这两个方法

代码示例

package org.westos_01;

public class Student {

	private String name;
	private int age;

	public Student() {
		super();
	}

	public Student(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 int hashCode() {
		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;
		Student other = (Student) obj;// 进行向下转型
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))// !(false)==true 进入if中 返回fale
			return false;
		return true;
	}

}

public class SetDemo3 {

	public static void main(String[] args) {
		Set<Student> set = new HashSet<Student>();

		// 创建学生对象
		Student s1 = new Student("高圆圆", 27);
		Student s2 = new Student("高圆圆", 28);
		Student s3 = new Student("文章", 30);
		Student s4 = new Student("马伊琍", 39);
		Student s5 = new Student("高圆圆", 27);

		// 存储到集合中
		set.add(s1);
		set.add(s2);
		set.add(s3);
		set.add(s4);
		set.add(s5);

		// 遍历
		for (Student s : set) {
			System.out.println(s.getName() + "---" + s.getAge());
		}
	}
}

HashSet集合的add()方法源码分析

inteface Collection{
	
}


interface Set extends Collection{
	
	
}

class HashSet implements Set{
	private  HashMap<E,Object> map;
	
	//创建HashSet集合对象的时候,起始底层创建了一个HashMap集合对象
	 public HashSet() {
        map = new HashMap<>();
    }
    
    
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;			//add方法底层依赖于HashMap集合的
    }
}


class HashMap<K,V> implements Map<K,V>{
		
		public V put(K key, V value) {
      	  return putVal(hash(key), key, value, false, true);
   		}
   		
   		
   			//putVal方法
   		 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
                
        Node<K,V>[] tab; Node<K,V> p; int n, i;	//Node是一种键值对对象
        
        //判断哈希表示是为空,如果为空.有set集合元素的情况,哈希表不为空
        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;			//k--->传入的元素
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))	
                						//一些判断,主要还是eqauls()方法
                e = p;	/e =p = key 
            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			mapping:映射	(Servlet  servlet-mapping)
                V oldValue = e.value;		//e.value=key ="hello"	,"world","java","world","java"
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;			//返回的oldValue:永远是第一次存储的哪个元素
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
}
哈希方法
static final int hash(Object key) {	//传入的字符串元素		"hello","java","world" ,"hello"....
        int h;	//哈希码值 
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);		//无符号右移
        						底层依赖于第一个方法hashCode() 
        						
        						
        							计算机底层运算数据:补码
    }


Set<String> set = new HashSet<String>() ;

		set.add("hello") ;
		set.add("java") ;
		set.add("world");
		
		set.add("hello") ;
		set.add("java") ;
		set.add("world");
如果在开发中,要保证元素的唯一性,并且还要保证元素有序(存储和取出一致),使用LinkedHashSet集合
如果开发中要使用集合排序,使用TreeSet集合(红黑树结构)
  自然排序
   选择器排序 
LinkedHashSet集合:
底层由链接列表和哈希表组成
  元素的唯一性,是由哈希表决定的(hashCode()和equals())
  元素的迭代有序,存储和取出一致,是由链接链表决定

代码示例

package test;

import java.util.LinkedHashSet;

public class TestDemo5 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();
		linkedHashSet.add("hello");
		linkedHashSet.add("java");
		linkedHashSet.add("world");
		linkedHashSet.add("world");
		linkedHashSet.add("java");
		linkedHashSet.add("hello");
		linkedHashSet.add("java");
		linkedHashSet.add("world");
		linkedHashSet.add("world");
		linkedHashSet.add("java");
		linkedHashSet.add("hello");
		linkedHashSet.add("java");
		linkedHashSet.add("world");
		linkedHashSet.add("world");
		linkedHashSet.add("java");
		for (String string : linkedHashSet) {
			System.out.println(string);
		}

	}

}
TreeSet集合通常情况下是以自然顺序对集合中的元素进行排序
 
  TreeSet:
   可以保证元素唯一(不重复)并且元素有序(Integer类型的元素自然升序(有小到大排序))
   自然排序
   比较器排序
  

  给TreeSet集合存储以下元素:20,23,22,18,17,19,24

代码示例

package test;

import java.util.TreeSet;

public class TestDemo6 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet<Integer> ts = new TreeSet<Integer>();
		ts.add(20);
		ts.add(22);
		ts.add(18);
		ts.add(23);
		ts.add(24);
		ts.add(17);
		ts.add(19);
		ts.add(18);
		ts.add(24);
		ts.add(20);
		ts.add(22);
		ts.add(18);
		ts.add(23);
		ts.add(24);
		ts.add(17);
		ts.add(19);
		ts.add(18);
		ts.add(24);
		for (Integer integer : ts) {
			System.out.println(integer + " ");
		}

	}

}
  需求:使用TreeSet集合存储自定义对象(Student类型),并遍历!
   如何排序
   按照学生的年龄从小到大进行排序 (主要条件)
  唯一性:

   如果所有的成员变量的值是一样的,则认为是同一个对象

在自定义类型中必须实现Comparable接口,在类中重写ComparaTo()方法

代码示例

package org.westos_03;

import java.util.TreeSet;

/**
 * 需求:使用TreeSet集合存储自定义对象(Student类型),并遍历! 如何排序 按照学生的年龄从小到大进行排序 (主要条件) 唯一性:
 * 如果所有的成员变量的值是一样的,则认为是同一个对象
 */
public class TreeSetDemo2 {

	public static void main(String[] args) {
		// 创建TreeSet集合对象
		TreeSet<Student> ts = new TreeSet<Student>();// 无参构造自然排
		// 创建学生对象
		Student s1 = new Student("gaoyuanyuan", 27);
		Student s2 = new Student("liushishi", 38);
		Student s3 = new Student("gaoyuanyuan", 28);
		Student s4 = new Student("wanglihong", 35);
		Student s5 = new Student("wanglihong", 30);
		Student s6 = new Student("fengqingy", 38);
		Student s7 = new Student("gaoyuanyuan", 27);

		// java.lang.ClassCastException: org.westos_03.Student cannot be cast to
		// java.lang.Comparable
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);
		ts.add(s7);

		// 遍历
		for (Student s : ts) {
			System.out.println(s.getName() + "---" + s.getAge());
		}

	}
}

package org.westos_03;

//对于自定义的类型,要实现自然排序,自定义的类型必须实现Comparable
//实现接口中的方法,compareTo() :比较方法
public class Student implements Comparable<Student> {

	private String name;
	private int age;

	public Student() {
		super();
	}

	public Student(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 int compareTo(Student s) { // 源码 cmp = k.compareTo(t.key)
		// 主要条件:按照年龄从小到大进行排序
		int num = this.age - s.age; // 如果年龄相等,不一定是同一个人
		// 需要自己分析次要条件;
		// 年龄相同,姓名的内容不一定相同,比较姓名
		int num2 = num == 0 ? this.name.compareTo(s.getName()) : num; // 如果年龄不相同,则返回num(年龄差) num2=num;
		return num2;
	}

}

package org.westos_03;

import java.util.TreeSet;

/**
 * 需求:使用TreeSet集合存储自定义对象(Student类型),并遍历! 如何排序 按照学生的年龄从小到大进行排序 (主要条件) 唯一性:
 * 如果所有的成员变量的值是一样的,则认为是同一个对象
 */
public class TreeSetDemo2 {

	public static void main(String[] args) {
		// 创建TreeSet集合对象
		TreeSet<Student> ts = new TreeSet<Student>();// 无参构造自然排
		// 创建学生对象
		Student s1 = new Student("gaoyuanyuan", 27);
		Student s2 = new Student("liushishi", 38);
		Student s3 = new Student("gaoyuanyuan", 28);
		Student s4 = new Student("wanglihong", 35);
		Student s5 = new Student("wanglihong", 30);
		Student s6 = new Student("fengqingy", 38);
		Student s7 = new Student("gaoyuanyuan", 27);

		// java.lang.ClassCastException: org.westos_03.Student cannot be cast to
		// java.lang.Comparable
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);
		ts.add(s7);

		// 遍历
		for (Student s : ts) {
			System.out.println(s.getName() + "---" + s.getAge());
		}

	}
}
TreeSet集合存储自定义对象(Student)
  按照姓名的长度从小到大进行排序 :主要条件

代码示例

package org.westos_04;

public class Student implements Comparable<Student> {

	private String name;
	private int age;

	public Student() {
		super();
	}

	public Student(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;
	}

	// TreeSet集合保证元素的唯一性,看返回值是否为0
	@Override
	public int compareTo(Student s) {
		// return 0;
		// 按照姓名的长度进行比较(默认从小到大)
		int num = this.getName().length() - s.getName().length();

		// ( 自己分析)次要条件

		// 如果姓名的长度一样,还要比较他们的内容是否相同
		int num2 = num == 0 ? this.getName().compareTo(s.getName()) : num;

		// 如果姓名的长度和姓名的内容都一致,也不能保证就是同一个人,还要比较年龄
		int num3 = num2 == 0 ? this.age - s.age : num2;
		return num3;
	}

}
package org.westos_04;

import java.util.TreeSet;

/*TreeSet集合存储自定义对象(Student)
* 
* 	按照姓名的长度从小到大进行排序 :主要条件*/
public class TreeSetDemo {

	public static void main(String[] args) {

		// 创建TreeSet集合,实现对该类型自然排序
		TreeSet<Student> ts = new TreeSet<Student>();

		// 创建学生对象
		Student s1 = new Student("gaoyuanyuan", 27);
		Student s2 = new Student("zhangguorong", 29);
		Student s3 = new Student("wuqilong", 40);
		Student s4 = new Student("liushishi", 28);
		Student s5 = new Student("fengqingy", 29);
		Student s6 = new Student("gaoyuanyuan", 22);
		Student s7 = new Student("gaoyuanyuan", 27);

		// 添加元素
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);
		ts.add(s7);

		// 遍历
		for (Student s : ts) {
			System.out.println(s.getName() + "---" + s.getAge());
		}

	}
}
TreeSet集合的构造方式不同,使用的排序也不同
  自然排序:自定义的类实现Compareable接口,然后创建TreeSet对象,通过无参构造形式创建对象
  比较器排序 :public TreeSet(Comparator<E> comparator)
                比较器排序的两种方式:
  1)自定义一个类,该类实现Comparator接口,重写Comparator接口中的compare()方法
  2)直接使用接口的匿名内部类
 TreeSet集合保证元素唯一,是看返回值是否为0 
  保证元素进行排序,两种排序方式
 通过比较器排序的方式去遍历Student对象,并且按照姓名长度进行排序

代码示例

package org.westos.comparator;

import java.util.Comparator;

public class MyComparator implements Comparator<Student> {

	@Override
	public int compare(Student o1, Student o2) {
		// TODO Auto-generated method stub
		// return 0;
		int num1 = o1.getName().length() - o2.getName().length();
		int num2 = num1 == 0 ? o1.getName().compareTo(o2.getName()) : num1;
		int num3 = num2 == 0 ? (o1.getAge() - o2.getAge()) : num2;
		return num3;
	}

}

package org.westos.comparator;

public class Student {
	private String name;
	private int age;

	public Student() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Student(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;
	}

}
package org.westos.comparator;

import java.util.Comparator;
import java.util.TreeSet;

import org.westos_05.Student;

public class TreeSetDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// TreeSet<Student> treeSet = new TreeSet<Student>(new MyComparator());
		TreeSet<Student> treeSet = new TreeSet<Student>(new Comparator<Student>() {

			@Override
			public int compare(Student o1, Student o2) {
				// TODO Auto-generated method stub
				// return 0;
				int num1 = o1.getName().length() - o2.getName().length();
				int num2 = num1 == 0 ? o1.getName().compareTo(o2.getName()) : num1;
				int num3 = num2 == 0 ? (o1.getAge() - o2.getAge()) : num2;
				return num3;
			}
		});
		Student s1 = new Student("gaoyuanyuan", 27);
		Student s2 = new Student("zhangguorong", 29);
		Student s3 = new Student("wuqilongdaw", 40);
		Student s4 = new Student("liushishi", 28);
		Student s5 = new Student("fengqingy", 29);
		Student s6 = new Student("gaoyuanyuan", 22);
		Student s7 = new Student("gaoyuanyuan", 27);
		treeSet.add(s1);
		treeSet.add(s2);
		treeSet.add(s3);
		treeSet.add(s4);
		treeSet.add(s5);
		treeSet.add(s6);
		treeSet.add(s7);
		for (Student student : treeSet) {
			System.out.println(student.getName() + "----" + student.getAge());
		}

	}

}







猜你喜欢

转载自blog.csdn.net/qq_40727767/article/details/80286838