Set和Map

Set和Map

第一节:Set接口

常用方法:

方法名描述
boolean add(E e)确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
void clear()移除此 collection 中的所有元素(可选操作)。
boolean contains(Object o)如果此 collection 包含指定的元素,则返回true。
boolean containsAll(Collection<?> c)如果此 collection 包含指定 collection 中的所有元素,则返回 true。
boolean equals(Object o)比较此 collection 与指定对象是否相等。
boolean isEmpty()如果此 collection 不包含元素,则返回true。
iterator()返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o)从此 collection 中移除指定元素的单个实例,如果存在的话(可选作)。
boolean removeAll(Collection<?> c)移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
int size()返回此 collection 中的元素数。
Object[] toArray()返回包含此 collection 中所有元素的数组。

存储特点:

相对无序,不可以存储相同的元素,不可以通过下标访问

public class Demo1 { 
    public static void main(String[] args) { 
        //创建对象 
        Set<String> set=new HashSet<String>(); 
        //1添加 
        set.add("菊花"); 
        set.add("枸杞"); 
        set.add("红枣"); 
        set.add("人参"); 
        set.add("灵芝"); 
        set.add("枸杞"); 
        System.out.println("元素个数:"+set.size()); 
        System.out.println(set); 
        //2删除 
        //2.1删除一个 
        // set.remove("灵芝"); 
        // System.out.println("删除之后:"+set); // 
        //2.2清空 
        // set.clear(); 
        //3遍历 
        //3.1foreach 
        System.out.println("--------增强for----------");
        for (String string : set) {
            System.out.println(string); 
        }
        //3.2使用迭代器
        System.out.println("---------迭代器-------"); Iterator<String> 
        it=set.iterator(); 
        while(it.hasNext()) { 
            System.out.println(it.next()); 
        }
        //4判断
        System.out.println(set.contains("菊花")); 
        System.out.println(set.contains("梅花")); 
    } 
}

Set常用的实现类:

  1. HashSet
此类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。
它不保证set的迭代顺序;特别是它不保证该顺 序恒久不变。
此类允许使用null元素。
Hash:哈希——实际含义散列,就是一种算法,把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散 列值。
哈希表:数组加链表,既有数组的优点也有链表的优点。

存储特点: 相对无序存储,不可以存储相同元素(排重),通过哈希表实现的集合
  1. 重写hashCode()
hashCode()是Object中的方法,每个对象的hashCode值是唯一的,所以可以理解成hashCode值表示这个对象在 内存中的位置

字符串String的hashCode(),是根据内容计算的。

HashSet集合排重时,需要判断两个对象是否相同,对象相同的判断可以通过hashCode值判断,所以需要重写 hashCode()方法
  1. 重写equals()
equals()方法是Object类中的方法,表示比较两个对象是否相等,若不重写相当于比较对象的地址, 所以我们可以尝试重写equals方法,检查是否排重 

  1. HashSet集合实现排重
HashSet的重复依据: hashCode和equals 
需要同时重写hashCode和equals方法,实现排重

案例:设计一个Student类,同时重写hashCode和equals方法,检查是否实现排重
public class Student { 
    private String name; 
    public Student(String name) { 
        super(); 
        this.name = name; 
    }
    public Student() { 
        super(); 
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    @Override 
    public String toString() { 
        return "Student [name=" + name + "]"; 
    }
    @Override 
    //重写equals 
    public boolean equals(Object obj) { 
        //先判断传入的参数对象是否是Student对象,若不是直接返回false 
        if(obj instanceof Student) { 
            //若是,强转成Student对象,并比较属性的值
            Student s = (Student) obj; 
            if(this.name.equals(s.name)) { 
                //若属性的值相同,则返回true 
                return true; 
            } 
        }
        return false; 
    }
    @Override 
    public int hashCode(){ 
        /*hashCode方法返回值是int类型,所以重写时需要找到int类型的数据返回,还
        要保证此方法的返回值 与对象的所有属性都相关,所以返回姓名属性的字符串的长度
        */
        return this.name.length(); 
    }
}
  1. LinkedHashSet
LinkedHashSet类是具有可预知迭代顺序(相对有序)的Set接口的哈希表和链接列表实现。
是HashSet的子类。 

存储特点: 有序存储,不可以存储相同元素(排重),通过链表实现的集合的有序。

LinkedHashSet集合的元素排重与HashSet集合排重方法一致
  1. TreeSet
TreeSet集合是可以给元素进行重新排序的一个Set接口的实现。
使用元素的自然顺序对元素进行排序,或者根据创 建 set 时提供的Comparator进行排序,
具体取决于使用的构造方法。 

存储特点: 无序存储,排重,通过二叉树实现的集合,可以给元素进行重新排序
  1. SortedSet

    TreeSet除了实现了Set接口外,还实现了SortedSet接口

方法名描述
first()返回此 set 中当前第一个(最低)元素。
last()返回此 set 中当前最后一个(最高)元素。
headSet(E toElement)返回此 set 的部分视图,其元素严格小于toElement。
tailSet(E fromelement)返回此 set 的部分视图,其元素大于等于fromElement。
subSet(E fromElement, E toElement)返回此 set 的部分视图,其元素从 fromElement(包括)到toElement(不包括)。
  1. TreeSet的自然排序
元素所属的类需要实现java.lang.Comparable接口,并重写compareTo方法。

compareTo方法除了可以进行排序外,还有排重的功能,但是必须在compareTo方法中对类中
所有的属性值都进行 判断,否则不比较那个属性,排重就会忽略哪个属性
public class Person implements Comparable<Person> {
    private String name; 
    private int age; 
    public Person() {
        super(); 
    }
    public Person(String name, int age) { 
        super(); 
        this.name = name; 
        this.age = age; 
    }
    @Override 
    public String toString() { 
        return "Person [name=" + name + ", 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 
    //重写compareTo方法,按照年龄升序,若年龄相同按照姓名降序排序 
    public int compareTo(Person o) { 
        //Person中有两个属性,此方法中必须对name和age两个属性都进行比较,
        //否则将无法正确排重 
        if(this.age != o.age) {
            return this.age - o.age; 
        }else { 
            return o.name.compareTo(this.name); 
        } 
    } 
}
  1. TreeSet的定制排序
元素需要通过java.util.Comparator接口(比较器)中的compare方法进行比较大小,并排
序。 

compare方法除了可以进行排序外,还有排重的功能,但是必须在compare方法中对类中所有
的属性值都进行判断, 否则不比较那个属性,排重就会忽略哪个属性 

TreeSet集合中的无参数构造方法默认使用自然排序的方式对元素进行排序,使用TreeSet集
合的定制排序时,创建集合对象不可以直接使用无参数构造方法,需要使用传入一个Comparator比较器的构造方法创建集合对象。
public class Animal { 
    private String name; 
    private int age; 
    @Override 
    public String toString() { 
        return "Animal [name=" + name + ", 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; 
    }
    public Animal(String name, int age) { 
        super(); 
        this.name = name; 
        this.age = age; 
    }
    public Animal() { 
        super(); 
    } 
}

public class AnimalDemo{ 
    public static void main(String[]args){ 
    //创建一个TreeSet集合,使用Comparator接口的匿名内部类的匿名对象作为比较器 
        TreeSet<Animal> treeSet = new TreeSet<>(new Comparator() {
            @Override 
            public int compare(Animal o1, Animal o2) {
                if(o1.age!=o2.age) { 
                    return o2.age - o1.age; 
                }else {
                    return o1.name.compareTo(o2.name); 
                } 
            } 
        }); 
        //添加元素 
        treeSet.add(new Animal("大黄", 1)); 
        treeSet.add(new Animal("旺财", 2)); 
        //遍历集合 
        Iterator<Animal> it = treeSet.iterator(); 
        while(it.hasNext()){ 
            System.out.println(it.next()); 
        } 
    } 
}
第二节:Map接口
Map接口是将键映射到值的对象。一个映射不能包含重复的键,每个键最多只能映射到一个值,
值可以重复。 
cn--->中国 
usa--->美国 
uk--->英国 
us--->美国 
cn--->中华人民共和国
方法名描述
clear()从此映射中移除所有映射关系(可选操作)。
containsKey(Object key)如果此映射包含指定键的映射关系,则返回 true。
containsValue(Object value)如果此映射将一个或多个键映射到指定值,则返回 true。
entrySet()返回此映射中包含的映射关系的 Set集合。
equals(Object o)比较指定的对象与此映射是否相等。
get(Object key)返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
hashCode()返回此映射的哈希码值。
isEmpty()如果此映射未包含键-值映射关系,则返回 true。
keySet()返回此映射中包含的键的 Set 集合
put(K key, V value)将指定的值与此映射中的指定键关联(可选操作)
putAll(Map<? extends K,? extends V> m)从指定映射中将所有映射关系复制到此映射中(可选操作)。
remove(Object key)如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
size()返回此映射中的键-值映射关系数。
/**
  * Map集合的使用 
  * 1 存储的键值对,键不能重复,一个键对应一个值,值可以重复。 
  * 2 无序 
*/
public class Demo1 { 
    public static void main(String[] args) { 
        //创建集合 
        Map<String, String> map=new HashMap<>();
        //1添加元素 
        map.put("cn", "中国"); 
        map.put("usa", "美国"); 
        map.put("uk","英国"); 
        map.put("kor", "韩国");
        map.put("cn", "中华人民共和国");//会把原来的值覆盖掉 
        //map.put("us", "美国"); 
        
        System.out.println("元素个数:"+map.size()); 
        System.out.println("打印:"+map.toString()); 
        
        //2删除 
        //map.remove("uk"); 
        //System.out.println("删除之后:"+map.toString()); 
        ///map.clear(); 
        
        //3遍历 
        //3.1使用keySet遍历 
        System.out.println("---------keySet()----------"); 
        Set<String> keySet = map.keySet();
        for (String key : keySet) { 
            System.out.println(key+"..........."+map.get(key)); }
        
        //3.2使用entrySet();
        System.out.println("---------entrySet()---------"); 
        Set<Entry<String, String>> entrySet =map.entrySet();
        //Entry(映射对)
        for (Entry<String, String> entry : entrySet){
           System.out.println(entry.getKey()+"...."+entry.getValue()); 
        }
        
        //4判断 
        //4.1isEmpty 
        System.out.println("判断isEmpty():"+map.isEmpty()); 
        
        //4.2判断是否包含指定的key 
        System.out.println("cn:"+map.containsKey("cn")); 
        
        //4.3判断是否包含指定的value 
        System.out.println("韩国:"+map.containsValue("韩国")); 
    } 
}

常用类:

  1. HashMap
基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,
并允许使用null值和null键。 此类不保证映射的顺序。

存储特点: 相对无序存储,元素以键值对形式存在,键不可以重复,值可以重复,
元素整体排重,可以快速的通过键查找到 所对应的值,通过哈希表实现的集合。

HashMap集合的排重,只需要重写键所属的类的hashCode和equals方法即可。

map集合中若向集合中添加相同键的键值对时,新的值会将旧的值覆盖。

在这里插入图片描述

  1. LinkedHashMap
LinkedHashMap集合是具有可预知迭代顺序的Set接口的哈希表和链接列表实现。此实现与
HashSet的不同之外在 于,后者维护着一个运行于所有条目的双重链接列表。
用法与HashSet类似。

存储特点: 有序存储,元素排重,通过链表实现的集合。
  1. HashTable
此类实现一个哈希表,该哈希表将键映射到相应的值。
任何非null对象都可以用作键或值。
Hashtable有一个子类Properties,Properties集合使用的比较频繁。

存储特点: 相对无序存储,元素排重,通过哈希表实现的集合

Map集合的遍历:

  1. 使用keySet方法与get方法结合
public class Demo { 
    public static void main(String[] args){
        HashMap<String, Integer> map = new HashMap<>(); 
        map.put("aaa", 12); 
        map.put("bbb", 13); 
        map.put("ccc", 14); 
        //通过keySet获取map中所有键 
        Set<String> set = map.keySet(); 
        //获取set的迭代器 
        Iterator<String> it = set.iterator(); 
        while(it.hasNext()){ 
            String s = it.next(); 
            //通过迭代的键,找到对应的值,一起输出 
            System.out.println(s+"---"+map.get(s)); 
        } 
    } 
}
  1. 使用Map.Entry方法

    调用Map集合的entrySet方法,相当于将Map集合转成一个Set集合,再通过Set集合的遍历方式遍历即可。

public class Demo { 
    public static void main(String[] args) { 
        HashMap<String, Integer> map = new HashMap<>(); 
        map.put("aaa", 111);
        map.put("bbb", 222); 
        map.put("ccc", 333); 
        //将map转成一个Set集合 
        Set<Map.Entry<String, Integer>> set = map.entrySet(); 
        //遍历set 
        Iterator<Map.Entry<String, Integer>> it = set.iterator();
        while(it.hasNext()) { 
            System.out.println(it.next()); 
        } 
    } 
}
第三节:Collections工具类

此类完全由在 Collection 上进行操作或返回 Collection 的静态方法组成。对集合进行

操作时,可以使用这个类中的静态方法。

常用方法:

1. 排序
/* static <T extends Comparable<? super T>> void sort(List<T> list) 
	根据元素的自然顺序 对指定列表按升序进行排序。 
*/
    ArrayList<Integer> list = new ArrayList<>(); 
	list.add(-10); 
	list.add(5); list.add(3); 
	list.add(7); 
	System.out.println(list); 
	Collections.sort(list);//默认升序 
	System.out.println(list);

2.将集合中的元素进行反转
    /*
    	static void reverse(List<?> list) 反转指定列表中元素的顺序。 		*/
    Collections.reverse();

3.将集合中的元素打乱
    /*
    static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换。 
    */
    
4.获取集合中的最大值和最小值
    /*
    static T max(Collection coll) 
    根据元素的自然顺序,返回给定 collection 的最大元素。 
    static <T extends Object & Comparable<? super T>>   					T min(Collection<? extends T> coll) 
    根据元素的自然顺序 返回给定 collection 的最小元素。 
    */
    int n1 = Collections.max(list)
5. 替换
    /*
    static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 
    使用另一个值替换列表中出现的所有某一指定值 
    */
    //原因:List集合是不排重的,使用新的元素将集合中出现的所有的旧的元素替换掉
    Collections.replaceAll(list,5,100);
6. 统计指定元素在集合中出现的次数
    /*static int frequency(Collection<?> c, Object o) 
    返回指定 collection 中等于指定对象的元素数。 
    */
    int num = Collections.frequency(list,5);
7.二分法查找
    /*
    static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 
    使用二分搜索法搜索指定列表,以获得指定对象。 
    */
    //前提:必须是排好序的集合 
    int index = Collections.binarySearch(list,-10); 
	//注意:Collections工具类中的方法只操作Collection接口,主要操作的是List接口

8.集合和数组之间的转换
    //1数组转成集合,集合不能做添加、删除操作 
    public static void arrayToList() {
    String[] names= {"张三","李四","王五"}; 
    List<String> list=Arrays.asList(names); 
    System.out.println("list:"+list); 
    //添加 //list.add("赵六"); 
    //删除 //list.remove(0); 
    //System.out.println("添加或删除之后list:"+list); 
    list.set(0, "本伟"); 
    System.out.println("替换之后:"+list); 
}

	//2 集合转成数组 
	public static void listToArray() { 
        ArrayList<String> arrayList=new ArrayList<>();
        arrayList.add("苹果"); 
        arrayList.add("小米"); 
        arrayList.add("华为"); arrayList.add("三星"); 
        //数组 
        String[] arr=arrayList.toArray(new String[0]); 
        System.out.println("--------遍历数组-------");
		for (String string : arr) { 
            System.out.println(string); 
        } 
    }

	//3特殊 
	public static void arrayToList2() { 
        Integer[] nums= {10,20,5,8,9}; 
        List<Integer> arraylist=Arrays.asList(nums);
        for (Integer integer : arraylist) { 
            System.out.println(integer); 
        } 
    } 
}
总结:
Collection 父接口 
	|_____List (特点:有序的,可以重复) 
		|___ArrayList (存储结构:数组,适合遍历查找) 
		|___LinkedList (链表,适合添加和删除) 
		|___Vector 数组 
		|___Stack 数组(栈)先进后出
	|_____Set(特点:无序的,不能重复) 
		|_____HashSet 哈希表(数组+链表+二叉树) 
				重复依据:1 执行hashCode()来计算存储的位置 
						2 执行equals比较结果 
		|_____LinkedHashSet 哈希表 可以保证顺序 
		|_____TreeSet 自平衡红黑二叉树 
			1 元素要实现Comparable接口 
			2 定制比较器 Comparator 重复依据:
				Comparable接口的compareTo方法的返回值0,或比较器的返回为0
                
Map(特点:1存储键值对,一个键对应一个值,键不能重复,值可以重复 2 无序) 
|______ HashMap 哈希表 
		1 执行hashCode()来计算存储的位置 ,
		2 执行equals比较结果 
|______ Hashtable 哈希表 不能存储null键和null值,线程安全的 jdk1.0
		--Properties 
|_______LinkedHashMap 哈希表 可以保证顺序 
|_______TreeMap 自平衡红黑二叉树 
		1 key 要实现Comparable接口 ,
		2 定制比较器 Comparator 
		
Collections工具类的使用。
Set
   特点: 无序 ,无下标 ,不重复
   
   set 没有特有方法。都是继承于父接口。
   
   底层通过哈希计算 在内存的地址。
   
   相同对象,哈希值一定相同。不同对象尽可能不一样。换句话说有可能相同。
   
   ①HashSet 作为主要实现类
       
	   1. 判断hashCode 是否相同,如果不同,直接存储。当不重复处理
	   
	   2. 当hashCode 相同时,它才会进行equals比较 内容,当内容相同了,不添加。

   ② TreeSet  实现 自然排序  
   
            要求 增加到该集合里的类 必须支持排序
   
            必须要实现Comparable 接口   否则汇报类型转换异常
			
			实现Comparable接口  要重写 CompareTo() 方法 ,确定比较的依据。
			
			自然排序和定制排序相比:定制排序的优先级高,而且和类解耦!!!
	③LinkedHashSet 基于链表的结构  多维护一层添加顺序。//按照添加的顺序输出内容
	
Map 
	    遍历:
		
		所有的key 
		        keySet() ==> Set 集合   
				
	    所有的value
		        values()  ==》 Collection集合
	
	    所有的键值对
		        entrySet  == >  Set集合 
				    
				在得到键值对后    键值对的类型  Map.Entry 类型
				       getKey() 获取该键值对 的 键
					   getValue() 获取该键值对的 值

 HashMap  的底层数据结构:
	
	        数组 +  链表 + 红黑树
			
			  new HashMap()
			
			(1)  加载因子      loadfactor = 0.75f
			 
			(2) Node<K,V>[] table  ==》 tab
			
			(3)resize() 初始长度为 16 DEFAULT_INITIAL_CAPACITY = 1 << 4;
			
	Node[] 数组 长度 初始为 0 
		第一次调用put 长度为16 ; 
		数组里的元素  Node{
		            1. key
				    2. value
				    3. hash
				    4. next
		             }
	每一个Node节点 存储时 是通过  key值的哈希值 %16,得到要存储的具体下标[0 15]
	Node 节点包括四部分 k v next hash  ,Node 相当于每个entry对象(键值对),实现了Map.entry接口
		30 %16   14 
		14 %16   14
	对于,存储下标相同时,判断equals 如果是key的哈希值以及equals都一样,key相同,后来的覆盖前面的。
	如果下标相同,key内容不相同,通过链表的方式挂载节点。注意:不会无穷挂载,会在节点数等于8时,转成红黑树(平衡二叉树)。当二叉树超过6层后又转成链表。
	HashMap 在设计 对key的哈希算法,尽量让 不同的key 存储在不同的下标,当数组 不够存储时,扩容机制。
	是在 原数组长度*0.75时(达到阈值时) 扩容 原数组长度*2  
	比如:  Node[] table  16   
	当存储的元素在数组中达到 16*0.75 ==12,扩容为 16*2 32 ;
	下次再扩容 当  32*0.75 24 时, 扩容为 32*2 ==64
	

 HashSet 的构造方法 ,
	  底层是 HashMap 
	   public HashSet() {
        map = new HashMap<>();
       }
	Set  set  = new HashSet(); // map = new HashMap<>();
	当调用HashSet的add 方法时,其实调用的是map的put方法
	set.add()// ==> //map.put(e, PRESENT)==null;
	源码:
	   public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
	put 方法会把 set存进的值当 key 存储,value 是一个常量对象 new Object();
	
HashSet  怎么 去 重?
	
	1. HashSet 是通过 hashCode 和 equals 方法 辨别元素是否重复。
	
	2. 由于 HashSet的底层是HashMap ,这样在add元素时,会调用 map的put方法,
	    把Set集合中要存储的值当成 key处理,而key 是唯一的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值