Java学习笔记——Set接口和Map接口

Set接口和Map接口

1.了解Set集合的特点
2.掌握Set集合下常用实现类的使用
3.了解Map集合的特点
4.掌握Map集合下常用实现类的使用
5.掌握Map集合的遍历方式
6.掌握Collections工具类

第一节 Set接口

1.1 Set接口常用方法

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

1.2 存储特点

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

package com.qf.day16;

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

/**
 * Set接口
 * 特点:无序,不可重复
 * @author wgy
 *
 */
 public class Demo1 {
        public static void main(String[] args) {
            //创建对象
            Set set=new HashSet();
            //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 it=set.iterator();
            while(it.hasNext()) {
                System.out.println(it.next());
            }
            //4判断
            System.out.println(set.contains("菊花"));
            System.out.println(set.contains("梅花"));
        }
    }

1.3 Set常用实现类

1.3.1 HashSet

此类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null元素。

Hash:哈希——实际含义散列,就是一种算法,把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。

哈希表:数组加链表,既有数组的优点也有链表的优点。

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

1.3.2 重写hashCode()

hashCode()是Object中的方法,每个对象的hashCode值是唯一的,所以可以理解成hashCode值表示这个对象在内存中的位置

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

HashSet集合排重时,需要判断两个对象是否相同,对象相同的判断可以通过hashCode值判断,所以需要重写hashCode()方法

案例:设计一个Animal类,重写hashCode方法,向一个HashSet集合中添加Animal对象,

    检验是否排重(若所有属性都相同,视为相同元素)

代码实现:

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() {
        super();
    }
    public Animal(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    @Override
    /*重写hashCode()方法,单纯为了检查此方法是否可以实现排重效果,所以返回一个固定的值,使所有本类对象的hashCode值都是相同的*/
    public int hashCode() {
        return 1;
    }
}

向HashSet集合中添加多个Animal对象时,所有属性都相同时,并没有完成想要的排重效果;

所以只重写hashCode方法并不能实现我们想要的排重效果

1.3.3 重写equals()

equals()方法是Object类中的方法,表示比较两个对象是否相等,若不重写相当于比较对象的地址,

所以我们可以尝试重写equals方法,检查是否排重

案例:设计一个Animal类,重写equals方法,向一个HashSet集合中添加Animal对象,
    检验是否排重(若所有属性都相同,视为相同元素)

代码实现:

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() {
        super();
    }
    public Animal(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    @Override
    /*重写hashCode()方法,单纯为了检查此方法是否可以实现排重效果,所以返回true,使得所有本类对象使用equals方法比较时,都是相等的*/
    public boolean equals(Object obj) {
        return true;
    }
}

向HashSet集合中添加多个Animal对象时,所有属性都相同时,并没有完成想要的排重效果;

所以只重写equals方法,也不能完全实现我们想要的排重效果。

1.3.4 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();
    }
}

同时重写hashCode和equals两个方法,可以实现元素的排重效果

1.3.5 LinkedHashSet

LinkedHashSet类是具有可预知迭代顺序(相对有序)的Set接口的哈希表和链接列表实现。是HashSet的子类。

存储特点:
    有序存储,不可以存储相同元素(排重),通过链表实现的集合(注定相对有序)
LinkedHashSet集合的元素排重与HashSet集合排重方法一致。

1.3.6 TreeSet集合

TreeSet集合是可以给元素进行重新排序的一个Set接口的实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的Comparator进行排序,具体取决于使用的构造方法。 
存储特点:
    无序存储,排重,通过二叉树实现的集合,可以给元素进行重新排序

1.3.7 SortedSet接口

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

SortedSet接口中常用的方法:

方法名 描述
first() 返回此 set 中当前第一个(最低)元素。
last() 返回此 set 中当前最后一个(最高)元素。
headSet(E toElement) 返回此 set 的部分视图,其元素严格小于toElement。
tailSet(E fromElement) 返回此 set 的部分视图,其元素大于等于fromElement。
subSet(E fromElement, E toElement) 返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。

1.3.8 TreeSet集合的元素排序

自然排序

元素所属的类需要实现java.lang.Comparable接口,并重写compareTo方法。

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

案例:设计一个Person类,实现将Person对象添加到TreeSet集合中时,对所有的元素进行排序

代码实现:

public class Person implements Comparable {
    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);
        }
    }
}

定制排序

元素需要通过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 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 it = treeSet.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

Comparable和Comparator的区别?

1 集合元素需要实现Comparable接口,叫做自然排序,Comparator是比较器,实现定制排序。

2 Comparable中的compareTo()一个参数, Comparator中compare()两个参数,返回值都是int类型,

如果返回0,表示两个比较元素相同,如果大于0 ,前面大于后面,如果小于0,前面小于后面。

上机练习:按照字母的长度来排列字符串 ,如果长度一样 按照编码顺序排列

“dog” “hello” “beijing” “tianjin” “shanghai” “guangzhou”

package com.qf.day16_2;

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

/**
 * 上机练习:按照字母的长度来排列字符串  ,如果长度一样 按照编码顺序排列
 * @author wgy
 *"dog"   "hello"  "beijing"   "tianjin"   "shanghai"   "guangzhou"
 */
public class Demo1 {
    public static void main(String[] args) {
        TreeSet treeSet=new TreeSet(new Comparator() {
            @Override
            public int compare(String o1, String o2) {

                int n=o1.length()-o2.length();
                int m=o1.compareTo(o2);

                return n!=0?n:m;
            }
        });

        treeSet.add("beijing");
        treeSet.add("guangzhou");
        treeSet.add("shanghai");
        treeSet.add("tianjin");
        treeSet.add("hello");
        treeSet.add("dog");
        treeSet.add("no");

        System.out.println(treeSet);

    }
}

第二节 Map接口

2.1 概述

Map接口是将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
cn--->中国
usa--->美国
uk--->英国
us--->美国

2.2 Map接口的常用方法

方法名                                     描述                                  
  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 m)    从指定映射中将所有映射关系复制到此映射中(可选操作)。         
  remove(Object key)                        如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。      
  size()                                    返回此映射中的键-值映射关系数。                    

2.3 Map常用实现类

2.3.1 HashMap

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

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

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

代码实现:

    //Person作为键
    public class Person {
        private String name;
        private int age;
        public Person() {
            super();
        }
        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 + "]";
        }
        @Override
        public int hashCode() {
            return age + name.length();
        }
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof Person) {
                Person p = (Person) obj;
                if(p.name.equals(name)&&p.age==age) {
                    return true;
                }
            }
            return false;
        }
    }
    //Dog类作为值
    public class Dog {
        private String name;
        public Dog(String name) {
            super();
            this.name = name;
        }
        public Dog() {
            super();
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Dog [name=" + name + "]";
        }
    }
    //测试类
    public class Demo {
        public static void main(String[] args) {
            HashMap map = new HashMap<>();
            map.put(new Person("zhangsan", 12), new Dog("大黄"));
            map.put(new Person("lisi", 12), new Dog("旺财"));
            map.put(new Person("zhangsan", 12), new Dog("二哈"));

        }
    }
map集合中若向集合中添加相同键的键值对时,新的值会将旧的值覆盖。
上述代码中map集合中有两个键值对,分别为:张三-12---二哈,lisi-12---旺财

2.3.2 LinkedHashMap

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

存储特点:
    有序存储,元素排重,通过链表实现的集合。

2.3.3 Hashtable

此类实现一个哈希表,该哈希表将键映射到相应的值。任何非null对象都可以用作键或值。

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

2.3.4 HashMap与Hashtable的区别

1)Hashtable线程安全的,而HashMap线程不安全的
2)Hashtable中不允许存在null的键和null值,但是HashMap中允许null的键和null值

2.4 Map集合的遍历

2.4.1 使用keySet方法与get方法结合

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

2.4.2 使用Map.Entry方法:

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

代码实现:

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

小结:

Collection 父接口 集合中常用方法 add() remove() contains() size() :

特点:有些是有序的,有些事无序的,有些可重复,有些不可重复

-----List:特点:有序,可以重复

    ----ArrayList: 存储结构  数组     适合遍历查找

    ----LinkedList: 存储结构  链表    适合插入 删除 

    ----Vector 向量

    ----Stack:先进后出

   -----Set:   特点:无序 ,不能重复

    ----HashSet:存储结构: 哈希表      无序 ,不能重复:重复依据: hashCode()   equals()

    ----LinkedHashSet: 存储结构: 哈希表      有序

    ----TreeSet:存储结构:红黑自平衡二叉树   无序  ,排序 ,不能重复:重复依据:Comparable接口 compareTo();  返回值为0就是重复的。

        Comparator:比较器,compare( o1, o2){  }  如果返回值为0 重复元素

Map 特点:存储键值对 ,键不能重复,一个键对应一个值,值可以重复

----HashMap:存储结构:哈希表 ,无序 ,键不能重复:     重复依据: hashCode()   equals()

----LinkeHashMap:存储结构:哈希表 ,有序

----Hashtable: jdk1.0的出现的集合,不能存储null键和null值,线程安全的。

    ---Properties:没讲

    ----TreeMap:存储结构:红黑自平衡二叉树

Hash开头的存储结构哈希表 hashCode和equals

Tree开头的存储二叉树 排序 比较 Comparable Comparator

Link开头都是有序的

第三节 Collections工具类

此类完全由在 Collection 上进行操作或返回 Collection 的静态方法组成。对集合进行操作时,可以使用这个类中的静态方法。

3.1 Collections中常用方法

1.同步线程

/*
        static  Collection 
    synchronizedCollection(Collection c) 
          返回指定 collection 支持的同步(线程安全的)collection。 
static  List 
 synchronizedList(List list) 
          返回指定列表支持的同步(线程安全的)列表。 
static  Map 
 synchronizedMap(Map m) 
          返回由指定映射支持的同步(线程安全的)映射。 
static  Set 
 synchronizedSet(Set s) 
          返回指定 set 支持的同步(线程安全的)set。 
        */
        Collection c = Collections.synchronizedCollection(new ArrayList());
        List s = Collections.synchronizedList(new ArrayList());
        Map m = Collections.synchronizedMap(new HashMap());

2.排序

/*
        static > 
void sort(List list) 
          根据元素的自然顺序 对指定列表按升序进行排序。 
        */
        ArrayList 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);

3.将集合中的元素进行反转

        /*
        static void reverse(List list) 
          反转指定列表中元素的顺序。 
        */
        Collections.reverse();

4.将集合元素打乱

        /*
        static void shuffle(List list) 
          使用默认随机源对指定列表进行置换。 
        */

5.获取集合中的最大值、最小值

        /*
        static T max(Collection coll) 
          根据元素的自然顺序,返回给定 collection 的最大元素。

          static > 
        T min(Collection coll) 
          根据元素的自然顺序 返回给定 collection 的最小元素。 
        */
        int n1 = Collections.max(list);

6.替换

        /*
        static  boolean 
 replaceAll(List list, T oldVal, T newVal) 
          使用另一个值替换列表中出现的所有某一指定值 
        */
        //原因:List集合是不排重的,使用新的元素将集合中出现的所有的旧的元素替换掉
        Collections.replaceAll(list,5,100);

7.统计指定元素在集合中出现的次数

        /*
        static int frequency(Collection c, Object o) 
          返回指定 collection 中等于指定对象的元素数。 
        */
        int num = Collections.frequency(list,5);

8.二分法查找

        /*
        static  int 
 binarySearch(List> list, T key) 
          使用二分搜索法搜索指定列表,以获得指定对象。 
        */
        //前提:必须是排好序的集合
        int index = Collections.binarySearch(list,-10);

        //注意:Collections工具类中的方法只操作Collection接口,主要操作的是List接口 

9.集合和数组的转换

1 数组转成集合
    package com.qf.day16;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo2 {
    public static void main(String[] args) {
        //arrayToList();
        //listToArray();
         arrayToList2();
    }
    //1数组转成集合,集合不能做添加、删除操作
    public static void arrayToList() {
        String[] names= {"张三","李四","王五"};
        List 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 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 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
Map(特点:存储键值对,键不能重复,值可以重复,无序)  
  |______ HashMap  哈希表 1 执行hashCode()来计算存储的位置 ,2 执行equals比较结果
  |______ Hashtable 哈希表  不能存储null键和null值,线程安全的  jdk1.0
  |_______LinkedHashMap  哈希表  可以保证顺序
  |_______TreeMap  自平衡红黑二叉树  1 key 要实现Comparable接口 ,2 定制比较器 Comparator

 Collections工具类的使用。

你可能感兴趣的:(Java基础)