zl程序教程

您现在的位置是:首页 >  前端

当前栏目

Map和Set接口

2023-09-27 14:19:50 时间
  • 日考
    已知有类
    
    class Worker{
    	private String name;
    	private Integer age;
    	private Double salary;
    	public Worker(){};		// 无参构造
    	public Worker(String name,Integer age, Double salary){
    		this.age = age;
    		this.name = name;
    		this.salary = salary;
    	}
        // get/set略
    }
      创建三个Worker对象属性分别为
    阿森 18 3500.0
    啊云 28 5500.0
    啊黄 3  -2000.0      
    创建一个泛型集合存储三个Worker对象
    从集合中删除下标为1Worker对象      
    遍历集合打印Worker的name属性    
    

演示的代码如下:

package com.txw.test;

import java.util.ArrayList;
import java.util.List;
class Worker{
    private String name;
    private Integer age;
    private Double salary;

    // 无参构造
    public Worker(){

    }
    public Worker(String name,Integer age, Double salary){
        this.age = age;
        this.name = name;
        this.salary = salary;
    }

    // get/set
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }
}
public class Test{
    public static void main(String[] args) {
        /**
         创建三个Worker对象属性分别为
         阿森 18 3500.0
         啊云 28 5500.0
         啊黄 3  -2000.0
         创建一个泛型集合存储三个Worker对象
         从集合中删除下标为1的Worker对象
         遍历集合打印Worker的name属性
         */
        Worker w1 = new Worker("阿森",18,3500.0);
        Worker w2 = new Worker("啊云",28,5500.0);
        Worker w3 = new Worker("啊黄",3,-2000.0);
        List<Worker> list = new ArrayList<>();
        list.add(w1);
        list.add(w2);
        list.add(w3);
        list.remove(1);
        // 普通遍历
        for (int i = 0; i < list.size(); i++) {
            Worker worker = list.get(i);
            System.out.println(worker.getName());
        }
        // 增强for遍历
        for (Worker worker : list) {
            System.out.println(worker.getName());
        }
    }
}

1 Set接口

  • 概念:Set接口是Collection的子接口。
  • 特点:无序、无下标、不可重复的。
  • 方法:均继承自Collection。
    使用方式:
package com.txw.test;

import java.util.HashSet;
import java.util.Set;
public class TestSet {
    public static void main(String[] args) {
        // 创建一个Set集合
        Set<String> set  = new HashSet<>();
        // 存放元素
        set.add(new String("阿森"));
        set.add(new String("阿云"));
        set.add(new String("啊龙"));
        set.add(new String("旺哥"));
        set.add(new String("阿黄"));
        // 重复的对象
        set.add(new String("阿森"));
        // 获取有效元素个数
        System.out.println( set.size() );		// 3
        // 获取元素由于没有下标只能使用forEach遍历
        for(String str : set){
            System.out.println( str );
        }
    }
}

1.1 实现类

1.1.1HashSet
  • hash:散列表。
  • ArrayList:数组实现,查询快,增删慢。
    LinkedList:链表实现,查询慢,增删快。
  • Set:数组+链表实现,hash表。
  • hashCode:哈希码,Object中提供的方法,可以使用hashCode()方法获取一个对象的哈希码
    Object中的hashCode是根据内存地址生成的一个int类型整数。
  • hashCode生成原则:每个不同的对象尽量不一样,相同的对象一样。
    hashCode不仅决定了对象的存储位置,HashSet还要通过hashCode验证对象是否为重复对象。
  • HashSet验证重复的方式:
    1. 获取对象的hashCode 如果hashCode不同HashSet认为不是重复的对象。
    2. 如果hashCode相同,HashSet认为对象有可能重复,再次调用equals进行确认。
    
  • 由于Object中的hashCode根据地址生成的HashCode,所以地址不同HashSet就认为这是不同的对象,在程序设计过程中地址不同而属性相同我们也认为对象是重复的。
    覆盖Object中的HashCode,根据属性生成一个hashCode。
    覆盖形式例:
    class Student{
        String name;
        Integer age;
        Double score;
    
    	@Override
    	public int hashCode(){
            // 将所有属性的hashCode合并为一个,返回
        	return name.hashCode()+age.hashCode()+score.hashCode();
    	}
    }
    
  • HashSet存储元素时先调用hashCode如果hashCode一致会在调用equals,
    使用HashSet时如果存储自定义类型,务必覆盖hashCode于equals两个方法。
    class Student{
        String name;
        Integer age;
        Double score;
    	@Override
    	public int hashCode(){
            // 将所有属性的hashCode合并为一个,返回
        	return name.hashCode()+age.hashCode()+score.hashCode();
    	}
         @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return Objects.equals(name, student.name) && Objects.equals(age, student.age) && Objects.equals(score, student.score);
        }
    }
    
    程序设计过程中,hashCode与equals经常会一起(被)使用,所以有equals的地方也要添加hashCode。
    演示的代码如下:
package com.txw.test;

import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
public class TestHashSet {
    public static void main(String[] args) {
        Student stu1 = new Student("阿森",18,90D);
        System.out.println(stu1.hashCode());		// 356573597
        Student stu2 = new Student("阿云",28,100D);
        System.out.println(stu2.hashCode());		// 1735600054
        Student stu3 = new Student("阿森",38,10D);
        System.out.println(stu3.hashCode());`		// 1077334155
        Student stu4 = new Student("阿森",38,10D);
        System.out.println(stu4.hashCode());		// 1077334155
        Set<Student> set=  new LinkedHashSet<>();
        set.add( stu1 );
        set.add( stu2 );
        set.add( stu3 );
        set.add( stu4 );
        for (Student student : set) {
            System.out.println( student );
        }
    }
}
class Worker{
    String name;
    Integer age;
    Double salary;
    // 属性私有提供get/set
    // 有参、无参方法
    //toString,equals,hashCode
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Worker worker = (Worker) o;
        return Objects.equals(name, worker.name) && Objects.equals(age, worker.age) && Objects.equals(salary, worker.salary);
    }
    @Override
    public int hashCode() {
        // return name.hashCode() + age.hashCode() + salary.hashCode();
        return Objects.hash(name, age, salary);
    }
}
class Student{
    String name;
    Integer age;
    Double score;

    @Override
    public int hashCode(){
        return name.hashCode() + age.hashCode() + score.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        System.out.println("Student.equals");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name) && Objects.equals(age, student.age) && Objects.equals(score, student.score);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    public Student(String name, Integer age, Double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
}
1.1.2 LinkedHashSet
  • 特点:可以获取元素时维护插入的顺序,存入对象时依然无序LinkedHashSet会记录每个元素插入的顺序,获取元素时按照顺序获取。

1.2 Map接口

  • 特点:一个元素由两个对象构成,key键与value值,通过key操作value。
    无序、无下标、键不可重复、值可以重复。
  • 比如:
    key      value
    张 -----> zhang
    李 -----> li
    里 -----> li
    单 -----> dan,shan
    
1.2.1 常用方法
  • 比如:
    方法名作用
    put(K key , V value)向Map添加一组键值对,如果键已存在则覆盖
    remove(K key)根据key删除一组键值对
    get(K key)根据key获取一个value
    clear()清空
    containsKey(Object key)判断key是否存在
    containsValue(Object value)判断value是否存在
    size()返回键值对数量
    keySet()获取所有的键,返回值为Set
    values()获取所有的值,返回值为Collection
    entrySet()返回所有的entry(键值对),返回值为Set
1.2.2 Map集合的遍历
  1. 键遍历(常用)。
    获取所有的键。
    Map<Integer,String> map = new HashMap<>();
    // 键遍历,获取所有的键
    Set<Integer> keys =  map.keySet();
    // 遍历keys获取每一个键
    for (Integer key : keys) {
        System.out.print( key+" ------- " );
        // 利用key获取value
        String value = map.get( key );
        System.out.println(value);
    }
    
    1. 值遍历(了解)。
      获取所有的值。
    Map<Integer,String> map = new HashMap<>();
    // 值遍历
    // 获取所有的value
    Collection<String> values =  map.values();
    // 遍历
    for (String value : values) {
         System.out.println( value );
    }
    
  2. 键值遍历。
    获取所有的键值对。
    Map<Integer,String> map = new HashMap<>();
    // 获取所有的键值对
    // 获取所有的Map.Entry
    Set<Map.Entry<Integer,String>> entrys =  map.entrySet();
    // 遍历set集合获取每个Map.Entry
    for(Map.Entry<Integer,String> entry : entrys ){
         Integer key = entry.getKey();//获取key
         String value = entry.getValue();//获取value
         System.out.println(key+" ----- "+value);
    }
    
1.3 实现类
  1. HashMap
    特点:操作速度快,线程不安全,如果key为自定义类型必须覆盖hashCode与equals。
  2. Hashtable(用不着)
    特点:操作速度慢,线程安全。
  3. LinkedHashMap
    特点:操作速度快,线程不安全,可以维护插入顺序。
    演示的代码如下:
package com.txw.test;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TestMap {
    public static void main(String[] args) {
        //  key泛型 value泛型
        Map<Integer,String> map = new HashMap<>();
        // 学号(Integer)与学生姓名(String)
        map.put(8848,"阿森");
        map.put(1101,"阿云");
        map.put(1102,"阿伟");
        map.put(1103,"阿黄");
        // 根据学号查询学生姓名
        String name1 = map.get(1103);
        // System.out.println(name1);
        // 删除学号为8848的劣质学生
        map.remove(8848);
        // 查看键值对数量
        // System.out.println(map.size());
        // 如果key已存在则替换
        map.put(1101,"阿森");
       // System.out.println( map.get(1101) );
        // 查看有没有学号为1104的学生
        // System.out.println( map.containsKey(1104) );
        // 查看有没有叫阿森的学生
        // System.out.println( map.containsValue("阿森") );
        // Map<Integer,String> map = new HashMap<>();
        // 键遍历,获取所有的键
        /* Set<Integer> keys =  map.keySet();
        // 遍历keys获取每一个键
        for (Integer key : keys) {
            System.out.print( key+" ------- " );
            // 利用key获取value
            String value = map.get( key );
            System.out.println(value);
        }*/
        // 值遍历
        // 获取所有的value
        // Collection<String> values =  map.values();
        // 遍历
        // for (String value : values) {
        //    System.out.println( value );
       // }
        // 获取所有的键值对
        // 获取所有的Map.Entry
        Set<Map.Entry<Integer,String>> entrys =  map.entrySet();
        // 遍历set集合获取每个Map.Entry
        for(Map.Entry<Integer,String> entry : entrys ){
            Integer key = entry.getKey();			// 获取key
            String value = entry.getValue();		// 获取value
            System.out.println(key+" ----- "+value);
        }
    }
}

总解

1 List
  • 特点:有序、有下标、可重复。
  • 常用方法(增删改查):
    1. add(Object o ) , add(int index , Object o) 添加元素
    2. remove(Object o) , remove(int index )      删除元素
    3. set(int index , Object o)				  修改元素
    4. get(int index) 或 遍历						查看元素
    
  • 实现类:
    1. ArrayList 数组实现,查询快,增删慢,操作速度快,线程不安全。
    2. LinkedList 链表实现,查询慢,增删快,操作速度快,线程不安全。
    
2 Set
  • 特点:无序、无下标、不可重复。
  • 常用方法(增删改查):
    1. add(Object o)					添加元素
    2. remove(Object o)					删除元素
    改不了
    遍历								   查看元素
    
  • 实现类
  • 1. HashSet 操作速度快,线程不安全,实现方式 数组+链表,链表过长会转换为红黑树,
       使用对象的hashCode与equals方法过滤重复,务必保证存入的对象为自定义类型时,必须手动覆盖hashCode与equals否则无法过滤重复。
    2. LinkedHashSet 操作速度快,线程不安全,去重机制与HashSet一致   。
    
3 Map
  • 特点:无序,无下标,key不可以重复,value可以重复。
    内部的元素都是由key与value两个对象组成。
  • 常用方法(增删改查):
    1. put(K key,V value)        添加元素
    2. remove(K key )			 删除元素
    3. put(K key, V value)	     修改元素(如果元素以存在则替换)
    4. get(K key)				 查看元素(根据key获取value)
       键遍历					
       值遍历
       键值遍历
    
  • 实现类
    1. HashMap 操作速度快,线程不安全,key不可以重复,使用key对象的hashCode与equals过滤重复,如果key为自定义类型,务必保证覆盖hashCode与equals两个方法,否则无法过滤重复。
    2. LinkedHashMap 操作速度快,线程不安全,可以维护元素的插入顺序
    

如图所示:
在这里插入图片描述
在这里插入图片描述