Day09-集合

集合

  • 可以动态的保存任意多个类型,使用比较方便

  • 提供了一系列方便的操作对象:add、remove、set、get等

  • 使用集合添加,删除新元素的示意代码简洁了

  • 集合主要是两组(单列集合、双列集合)

  • collection接口有两个重要的子接口List Set ,他们的实现子类都是单列集合

  • Map接口的实现子类是双列集合,存放的K-V

单列集合

双列集合

  1. collection接口和常用方法

    • collection接口遍历元素方式1----使用Iterator(迭代器)

    • Iterator对象称为迭代器,主要用于遍历Collection集合中的的元素

    • 所有实现了Collection接口的集合类都有一个it erator()方法,用以返回一个实现了Iterator接口的对象,即可以返 回一个迭代器

    • 当退出while循环后,这时iterator迭代器,指向最后的元素

    • iterator.next();//NosuchElementException

    • 如果希望再次遍历,需要重置我们的迭代器


    package 集合;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    public class Demo01 {
        public static void main(String[] args) {
            Collection col = new ArrayList();
            col.add(new Book("红楼梦","曹雪芹",10));
            col.add(new Book("三国演义","施耐庵",6));
            Iterator iterator = col.iterator();
            while (iterator.hasNext()) {
                Object obj =  iterator.next();
                System.out.println(obj);
            }
          
          iterator=col.iterator();//重置迭代器
        }
    }
    class Book{
        private String name;
        private String author;
        private double price;
        public Book(String name, String author, double price) {
            this.name = name;
            this.author = author;
            this.price = price;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAuthor() {
            return author;
        }
        public void setAuthor(String author) {
            this.author = author;
        }
        public double getPrice() {
            return price;
        }
        public void setPrice(double price) {
            this.price = price;
        }
        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", author='" + author + '\'' +
                    ", price=" + price +
                    '}';
        }
    }
     

  • collection接口遍历元素方式2----增强for循环(底层还是迭代器),只能用来遍历集合或数组

package 集合;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo01 {
    public static void main(String[] args) {
        Collection col = new ArrayList();
        col.add(new Book("红楼梦","曹雪芹",10));
        col.add(new Book("三国演义","施耐庵",6));
        Iterator iterator = col.iterator();
        //增强for循环
        for (Object o : col) {
            System.out.println(o);
        }
    }
}
class Book{
    private String name;
    private String author;
    private double price;
    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}
 

例题1

分别用增强for循环和迭代器遍历集合元素

package 集合;
import java.sql.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class Demo02 {
    public static void main(String[] args) {
        List list =new ArrayList();
        list.add(new Dog("阿黄",1));
        list.add(new Dog("旺财",2));
        list.add(new Dog("来福",3));
        Iterator iterator = list.iterator();
        //迭代器
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println(obj);
        }
        System.out.println("====分割线====");
        //增强for循环
        for (Object o :list) {
            System.out.println(o);
        }
    }
}
class Dog{
    private String name;
    private int age;
    public Dog(String name, int age) {
        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 "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
 
  1. List接口

  • List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复

  • List集合中的每个元素都尤其对应的顺序索引,即支持索引

例题2

package 集合;

import javax.swing.*;
import java.time.temporal.Temporal;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ForkJoinPool;
public class Demo03 {
    public static void main(String[] args) {
        //方式1 
        List list = new ArrayList();
        
        //方式2
        List list = new LinkedList();
      
        //方式3
        List list = new Vector();
        
        list.add(new Book("红楼梦", "林黛玉", 13));
        list.add(new Book("三国演义", "曹操", 17));
        list.add(new Book("水浒传", "宋江", 11));
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println(obj);
        }
        System.out.println("==========排序后===========");
        bubble(list);
        iterator = list.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
    public static void bubble(List list) {
        int count = list.size();
        for (int i = 0; i < count - 1; i++) {
            for (int j = 0; j < count - 1 - i; j++) {
                Book book1 = (Book) list.get(j);
                Book book2 = (Book) list.get(j + 1);
                if (book1.getPrice() > book2.getPrice()) {
                    list.set(j, book2);
                    list.set(j + 1, book1);
                }
            }
        }
    }
}
 

注意点:arrayList是线程不安全的

  • transient:表示该属性不会被序列化

  • 在开发中,需要线程同步时,考虑使用vector

 底层结构版本线程安全(同步)扩容倍数
ArrayList 可变数组 jdk1.2 不安全,效率高 如果是有参构造1.5倍,如果是无参构造第一次10,从第二次开始按1.5扩
Vector 可变数组Object[] jdk1.0 安全,效率不高 如果是无参,默认10,满后就按照2倍扩容,如果指定大小,则每次直接按2倍扩

 

 底层结构增删的效率改查的效率
ArrayList 可变数组 较低,数组扩容 较高
LinkedList 双向链表 较高,通过链表追加 较低

3.set接口和常用方法

  • 无序(添加和取出的顺序不能保证 一致,但是取出的顺序是固定的),没有索引

  • 不允许重复元素,最多包含一个null

  • 和List接口一样,set接口也是collection的子接口,因此,常用方法和collection接口一样

  • 可以使用迭代器

  • 增强for

  • 不能使用索引的方式来获取(普通for循环无法实现 )

HashSet的底层机制

  • HashSet底层是HashMap,HashMap底层是(数组+(单向)链表+红黑树)

  • 添加一个元素是,先得到hash值-》会转成索引值

  • 找到存储数据表table,看这个索引位置是否已经存放的有元素,如果没有直接加入,如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后

  • 在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)

例题1

package Set;
import java.util.HashSet;
import java.util.Objects;
public class Demo01 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("小明",3));
        hashSet.add(new Employee("小红",4));
        hashSet.add(new Employee("小明",3));
        System.out.println("hashSet:"+hashSet);
    }
}
class Employee{
    private String name;
    private int age;
    public Employee(String name, int age) {
        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 "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age && Objects.equals(name, employee.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

 

例题2

package Set;
import java.util.HashSet;
import java.util.Objects;
public class Demo02 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee_("小刘",20000,new MyDate(1999,9,12)));
        hashSet.add(new Employee_("小张",15000,new MyDate(1998,10,20)));
        hashSet.add(new Employee_("小刘",15000,new MyDate(1999,9,12)));
        System.out.println("hashSet:"+hashSet);
    }
}
class Employee_{
    private String name;
    private double sal;
    private MyDate birthday;
    public Employee_(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSal() {
        return sal;
    }
    public void setSal(double sal) {
        this.sal = sal;
    }
    public MyDate getBirthday() {
        return birthday;
    }
    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }
    @Override
    public String toString() {
        return "Employee_{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee_ employee = (Employee_) o;
        return Objects.equals(name, employee.name) && Objects.equals(birthday, employee.birthday);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, birthday);
    }
}
class MyDate{
    private long year;
    private int month;
    private int day;
    public MyDate(long year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
    public long getYear() {
        return year;
    }
    public void setYear(long year) {
        this.year = year;
    }
    public int getMonth() {
        return month;
    }
    public void setMonth(int month) {
        this.month = month;
    }
    public int getDay() {
        return day;
    }
    public void setDay(int day) {
        this.day = day;
    }
    @Override
    public String toString() {
        return "'"+ year +
                "-" + month +
                "-" + day+"'";
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return year == myDate.year && month == myDate.month && day == myDate.day;
    }
    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }
} 
 

 

LinkedHashSet

  • LinkedHashSet是HashSet的子类

  • LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表

  • LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时是用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的

  • LinkedHashSet不允许添加重复元素

例题1

package Set;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Objects;
public class Demo03 {
    public static void main(String[] args) {
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("大众",15_0000));
        linkedHashSet.add(new Car("比亚迪",12_0000));
        linkedHashSet.add(new Car("大众",15_0000));
        System.out.println("linkedHashSet:"+ linkedHashSet);
    }
}
class Car{
    private String name;
    private double price;
    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

Map

  • Map与Collection并列存在,用于保存具有映射关系的数据:Key-value(双列元素)

  • Map中的key和value可以是任何引用类型,会封装到hashMap$Node对象中

  • Map中的key不允许重复,原因和hashSet一样

  • Map的key可以为null,value也可以为null,注意key为空,只能有一个,value为空可以有多个

  • 常用String类作为Map的key

  • key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value

  • Map存放数据的key-value示意图,一对k-v是放在一个HashMap$Node中的,有因为Node实现了Entry接口,有些书上也说一对k-v就是一个Entry(如图)

  • entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node,这是因为static class Node<K,V>implements Map.Entry<K,V>

  • 当把HashMap$Node对象存到entrySet就方便我们的遍历,因为Map.Entry提供了重要方法,K getKey();V getValue();

Map接口遍历方法

  1. containsKey:查找键是否存在

  2. keySet:获取所有的键

  3. entrySet:获取所有关系k-v

  4. values:获取所有的值

package Set;
import java.rmi.MarshalledObject;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class Demo04 {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("蔡徐坤", "杨颖");
        map.put(null, "张无忌");
        map.put("赵敏", null);
        map.put("袁湘琴", "江直树");
        //keySet获取所有键:先取出所有的key,再通过key取出对应的value
        //1. 增强for
        System.out.println("======keySet方式中的使用增强for遍历=========");
        Set keyset = map.keySet();
        for (Object key : keyset) {
            System.out.println(key + "-" + map.get(key));
        }
        System.out.println("======keySet方式中的使用迭代器遍历=========");
        //2.迭代器
        Iterator iterator =keyset.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            Object value = map.get(key);
            System.out.println(key+"-"+ value);
        }
        //entrySet获取所有的k-v
        //1.增强for
        Set entryset = map.entrySet(); //EntrySet<Map.Entry<k,v>>
        System.out.println("======entrySet方式中的使用增强for遍历=========");
        for (Object entry :entryset) {
            Map.Entry m =(Map.Entry) entry;
            System.out.println(m.getKey()+"-"+m.getValue());
        }
        //2.迭代器
        System.out.println("======entrySet方式中的使用迭代器遍历=========");
        Iterator iterator1 = entryset.iterator();
        while (iterator1.hasNext()) {
            Object entry =  iterator1.next();
            Map.Entry m=(Map.Entry) entry;
            System.out.println(m.getKey()+"-"+m.getValue());
            System.out.println(entry.getClass()); //HashNode$Node--->实现了Map.Entry(getKey,getValue)
        }
        //values获取所有的值
        //1.增强for
        Collection values = map.values();
        System.out.println("======values方式中的使用增强for遍历=========");
        for (Object val :values) {
            System.out.println(val);
        }
        //2.迭代器
        System.out.println("======values方式中的使用迭代器遍历=========");
        Iterator iterator2 = values.iterator();
        while (iterator2.hasNext()) {
            Object val =  iterator2.next();
            System.out.println(val);
        }
    }
}
 

例题1

package Set;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Demo05 {
    public static void main(String[] args) {
        Map map=new HashMap();
        map.put(1,new Worker(1,"文姬",21000));
        map.put(2,new Worker(2,"允浩",12000));
        map.put(3,new Worker(3,"民勇",18000));
        //keySet方式获取所有的key,并通过key获取values值
        //1.迭代器
        Set keyset = map.keySet();
        System.out.println("==========迭代器==========");
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            Worker worker = (Worker)map.get(key);
            if (worker.getSal()>18000) {
                System.out.println(map.get(key));
            }
        }
        //2.增强for
        System.out.println("=========增强for===========");
        for (Object key :keyset) {
            Worker worker=(Worker)map.get(key);
            if (worker.getSal()>18000) {
                System.out.println(map.get(key));
            }
        }
    }
}
class Worker {
    private int id;
    private String name;
    private double sal;
    public Worker(int id, String name, double sal) {
        this.id = id;
        this.name = name;
        this.sal = sal;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSal() {
        return sal;
    }
    public void setSal(double sal) {
        this.sal = sal;
    }
    @Override
    public String toString() {
        return "Worker{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sal=" + sal +
                '}';
    }
}
 

HashMap

  • HashMap是Map接口使用频率最高的实现类

  • HashMap是以key -val对的方式来存储数据(HashMap$Node类型)

  • key不能重复,但是值可以重复,允许使用null键和null值

  • 如果添加相同的key,则会覆盖原来的key-val,等同于修改(key不会替换,val会替换)

  • 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的

  • HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,即没有synchronized

HashTable

  • 如果添加相同的key,则会覆盖原来的key-val,等同于修改(key不会替换,val会替换),不允许使用null键和null值

  • 底层数组Hashtable$Entry[],初始化大小为11

  • 临界值threshold 8=11*0.75

  • 扩容:按照自己的扩容机制来进行即可

  • 执行方法addEntry(hash,key,value,index),添加k-v封装到Entry

  • 当if(count>=threshold)满足时 ,就进行扩容,按照int newCapacity=(oldCapacity<<1)+1;的大小扩容

 版本线程安全(同步)效率允许null键null值
hashMap 1.2 不安全 可以
hashTable 1.0 安全 较低 不可以

Properties

  • Properties类继承自HashTable类并实现了Map接口,也是使用一种键值对的形式来保存数据

  • 他的使用特点和HashTable类似

  • Properties还可以用于从x x x.properties文件中,加载数据到Properties类对象,并进行读取和修改

  • 说明:工作后x x x.properties文件通常作为配置文件

总结-开发过程中如何选择集合实现类

  1. 先判断存储的类型(一组对象【单列】或一组键值对【双列】)

  1. 一组对象:Collection接口

  • 允许重复:List

    • 增删多:LinkedList(底层维护了一一个双向链表)

    • 改查多:ArrayList(底层维护Object类型的可变数组,索引来定位速度快)

  • 不允许重复:Set

    • 无序:HashSet(底层是HashMap,维护了一个哈希表,即(数组+链表+红黑树))

    • 排序:TreeSet

      • 当我们使用无参构造器,创建TreeSet时,仍然是无序的

      • 希望添加的元素按照字符串大小来排序,使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类),并指定排序

    • 插入和取出顺序一致:LinkedHashSet(底层LinkedHashMap--》底层HashMap),维护数组+双向链表

  1. 一组键值对:Map

  • 键无序:HashMap【底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树】

  • 键排序:TreeMap

  • 键插入和取出顺序一致:LinkedHashMap

  • 读取文件:P roperties

例题1

package 集合;
import java.util.*;
public class Demo04 {
    private static List list;
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(new News("新冠确诊病例超千万,数百万印度教徒赴恒河\"圣浴\"引民众担忧"));
        list.add(new News("男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生"));
        int size = list.size();
        for (int i = size-1; i >=0 ; i--) {
            Object o = list.get(i);
            News news=(News)o;
            System.out.println(sortReverse(news.getTittle()));
        }
    }
    public static String sortReverse(String value){
        if(value.length()>15){
            value=value.substring(0,15)+"...";
        }else {
            value=value;
        }
        return value;
    }
}
class News{
    private String tittle;
    private String content;
    public News(String tittle) {
        this.tittle = tittle;
    }
    public String getTittle() {
        return tittle;
    }
    public void setTittle(String tittle) {
        this.tittle = tittle;
    }
    public String getcontent() {
        return content;
    }
    public void setcontent(String content) {
        this.content = content;
    }
    @Override
    public String toString() {
        return "tittle='" + tittle;
    }
}
 

例题2

package 集合;
import java.util.ArrayList;
public class Demo05 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        Car 宝马 = new Car("宝马", 400000);
        list.add(宝马);
        Car 宾利 = new Car("宾利", 5000000);
        list.add(宾利);
        System.out.println("list:"+list);
        list.remove(1);
        System.out.println("删除宾利:"+list);
        System.out.println("是否包含\"宝马\":"+list.contains(宝马));
        System.out.println("获取元素个数:"+list.size());
        System.out.println("是否为空:"+list.isEmpty());
        list.clear();
        ArrayList list1 = new ArrayList();
        list1.add(new Car("大众",140000));
        list1.add(new Car("比亚迪",1500000));
        System.out.println("list1:"+list1);
        list.addAll(list1);
        list.add(new Car("兰博基尼",4000000));
        list.add(new Car("五菱",100000));
        System.out.println("list:"+list);
        System.out.println("查找list1中的元素是否存在:"+list.containsAll(list1));
        System.out.println("删除list中的list1元素:"+list.removeAll(list1));
        for (Object o :list) {
            System.out.println(o);
        }
    }
}
class Car{
    private String name;
    private double price;
    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
 

例题3

//方式1
package 集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Demo06 {
    public static void main(String[] args) {
        Map map = new HashMap();
        Emp jack = new Emp("jack", 650);
        Emp tom = new Emp("tom", 1200);
        Emp smith = new Emp("smith", 2900);
        map.put(1,jack);
        map.put(2,tom);
        map.put(3,smith);
        System.out.println("当前员工信息为:"+map);
        Set keyset = map.keySet();
        for (Object key : keyset) {
            Object o = map.get(key);
            Emp emp=(Emp)o;
            if (emp.getName()=="jack"){
               emp.setSal(2600);
            }
            emp.setSal(emp.getSal()+100);
            System.out.println("修改后:"+key+"-"+map.get(key));
       }
    }
}
class Emp{
    private String name;
    private double sal;
    public Emp(String name, double sal) {
        this.name = name;
        this.sal = sal;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSal() {
        return sal;
    }
    public void setSal(double sal) {
        this.sal = sal;
    }
    @Override
    public String toString() {
        return "Emp{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                '}';
    }
}
//方式2
package 集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Demo06 {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("jack", 650);
        map.put("tom", 1200);
        map.put("smith", 2900);
        System.out.println("当前员工信息为:"+map);
        map.put("jack",2600);
        Set keyset = map.keySet();
        for (Object key : keyset) {
            map.put(key,(Integer)map.get(key)+100);
            System.out.println("修改后:"+key+"-"+map.get(key));
       }
    }
}
 

例题4

试分析HashSet和TreeSet分别如何实现去重的?

  • HashSet的去重机制:hashCode()+equals(),底层先通过存入对象,进行运算得到一个hash值,通过hash值得到对应的索引,如果发现table索引所在的位置,没有数据,就直接存放,如果有数据,就进行equals比较【遍历比较】,如果比较后,不相同就加入,否则就不加入

  • TreeSet去重机制:如果你传入了一个Comparator匿名对象,就是用实现的conpare去重,如果方法返回0,就认为是相同的元素/数据,就不添加,如果你没有传入一个Comparator匿名对象,则以你添加的对象实现的Compareable接口的compareTo去重

例题5

下面代码运行会不会抛出异常,并从源码层面说明原因

 
TreeSet treeSet = new TreeSet();
treeSet.add(new Person());
 

分析

add方法,因为TreeSet()构造器没有传入Comparator接口的匿名内部类,所以在底层Comparable<? super K> k=(Comparable<? super K>) key;即 把Person转成Comparable类型,Person没有实现Comparable接口,可以在Person类里实现Comparable接口,重写compareTo方法

例题6

下面的代码输出什么?
  已知:Person类按照id和name重写了hashCode和equals方法,问下面代码输出什么?
  HashSet set = new HashSet();
  Person p1 = new Person(1001,"AA");
  Person p2 = new Person(1002,"BB");
  set.add(p1);
  set.add(p2);
  p1.name = "CC";
  set.remove(p1); 
  System.out.println(set);
  set.add(new Person(1001,"CC"));
  System.out.println(set);
  set.add(new Person(1001,"AA"));
  System.out.println(set);
 

 

//代码实现
package 集合;
import java.util.HashSet;
import java.util.Objects;
public class Demo07 {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        Person p1 = new Person(1001,"AA");
        Person p2 = new Person(1002,"BB");
        set.add(p1);//成功
        set.add(p2);//成功
        p1.name = "CC";
        set.remove(p1); //失败
        System.out.println(set);
        set.add(new Person(1001,"CC")); //成功
        System.out.println(set);
        set.add(new Person(1001,"AA")); //成功
        System.out.println(set);
    }
}
class Person{
    public int id;
    public String name;
    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id && Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
posted @ 2023-06-16 20:18  仓鼠的气垫床  阅读(6)  评论(0编辑  收藏  举报