zoukankan      html  css  js  c++  java
  • java数据结构4--集合Set

     Set接口

     

    Set接口用来表示:一个不包含“重复元素”的集合
    Set接口中并没有定义特殊的方法,其方法多数都和Collection接口相同。

    重复元素的理解
    通常理解:拥有相同成员变量的对象称为相同的对象,如果它们出现在同一个集合中的话,称这个集合拥有重复的元素

    HashSet中对重复元素的理解:和通常意义上的理解不太一样!
    两个元素(对象)的hashCode返回值相同,并且equals返回值为true时(或者地址相同时),才称这两个元素是相同的。

    TreeSet中对重复元素的理解:元素的compareTo方法或者集合的比较器compare方法返回值为0则认为这两个元素是相同的元素。

    1 Set接口的方法

    可知Set接口并没有比父类Collection接口提供更多的新方法。

    2、HashSet类

    线程不安全,存取速度快
    它的大多数方法都和Collection相同
    它不保证元素的迭代顺序;也不保证该顺序恒久不变
    当HashSet中的元素超过一定数量时,会发生元素的顺序重新分配。

    2.1HashSet构造方法

        public HashSet() {
            map = new HashMap<>();
        }    
        public HashSet(Collection<? extends E> c) {
            map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
            addAll(c);
        }
        public HashSet(int initialCapacity, float loadFactor) {
            map = new HashMap<>(initialCapacity, loadFactor);
        }
        public HashSet(int initialCapacity) {
            map = new HashMap<>(initialCapacity);
        }
        HashSet(int initialCapacity, float loadFactor, boolean dummy) {
            map = new LinkedHashMap<>(initialCapacity, loadFactor);
        }

    看来set底层是HashMap

    2.2成员变量

    2.3成员方法

    2.4HashSet如何保证元素唯一?

    考查add(Object obj)方法的实现过程:

    1. 先调用obj的hashCode方法,计算哈希值(槽位值slot:bucket)
    2. 根据哈希值确定存放的位置
    3. 若位置上没有元素,则这个元素就是第一个元素,直接添加
    4. 若此位置上已经有元素,说明还有元素的hashCode方法返回值与它相同,则调用它的equals方法与已经存在的元素进行比较
    5. 若返回值为true,表明两个元素是“相同”的元素,不能添加
    6. 若返回值为false,表明两个元素是“不同”的元素,新元素将以链表的形式添加到集合中

        

    import java.util.HashSet;
    
    /*
     *自定义对象存储到HashSet中
     *
     *int hashCode:元素被添加时被调用,用于确认元素的槽位值
     *boolean equals:当发生碰撞时,调用被添加元素的equals方法和已经存在的元素进行比较,
     *    true:不能添加
     *    false:可以添加,多个元素占用一个槽位值.以链表形式存在.
     *
     */
    
    class Worker{
    //    static int i = 0;
        private String name;
        private int age;
        private String id;
        
        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 String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public Worker(String name, int age, String id) {
            super();
            this.name = name;
            this.age = age;
            this.id = id;
        }
        public Worker() {
            super();
            // TODO Auto-generated constructor stub
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((id == null) ? 0 : id.hashCode());
            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;
            Worker other = (Worker) obj;
            if (age != other.age)
                return false;
            if (id == null) {
                if (other.id != null)
                    return false;
            } else if (!id.equals(other.id))
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
        @Override
        public String toString() {
            return "Worker [name=" + name + ", age=" + age + ", id=" + id + "]";
        }
        
        //如何重写hashCode?尽量让所有的成员变量都参与到运算中.
        //name age id
        /*@Override
        public int hashCode() {
            int code1 = name.hashCode();
            int code2 = id.hashCode();
            return code1 * 3 + age + code2;
        }*/
        
        
        
    }
    
    public class HashSetDemo2 {
    
        public static void main(String[] args) {
            HashSet<Worker> set = new HashSet<>();
            
            Worker w1 = new Worker("tom1", 20, "001");
            Worker w2 = new Worker("tom1", 20, "001");
            Worker w3 = new Worker("tom2", 22, "003");
            Worker w4 = new Worker("tom2", 22, "003");
            Worker w5 = new Worker("tom3", 22, "003");//
            
            set.add(w1);
            set.add(w2);
            set.add(w3);
            set.add(w4);
            set.add(w5);
            
            for (Worker worker : set) {
                System.out.println(worker);
            }
            
        }
    
    }
    重写HashCode和equals方法

    HashSet注意事项:
    1.想要往HashSet中添加的对象,需要在定义类时,重写hashCode和equals方法
    2.由于HashSet使用的是散列算法,所以,轻易不要在迭代集合元素的时候改变集合中的元素

    2.5并发修改异常

    import java.util.HashSet;
    import java.util.Iterator;
    
    /*
     * 演示HashSet并发修改异常
     */
    public class HashSetDemo3 {
    
        public static void main(String[] args) {
            HashSet<String> set = new HashSet<String>();
            
            set.add("hello");
            set.add("hello2");
            set.add("world");
            set.add("world");
            
            Iterator<String> it = set.iterator();
            while(it.hasNext()){
                String str = it.next();
                if(str.equals("world")){
    //                set.remove("world");//ConcurrentModificationException
                    it.remove();
                }
            }
            
            for (String s : set) {
                System.out.println(s);
            }
            
            
            
        }
    
    }
    并发修改异常

     补充技能:

    3、LinkedHashSet类

    从后缀可以看出:其本质是HashSet,只不过在内部维护了一个链表,可以记住元素放入的顺序,这样就保证了存取的顺序,

    但是正是由于多了链表,所以它的效率低些.

    • 如何保证元素唯一性:hashCode, equals方法
    • 如何保证存取顺序性:链表

    没有特殊成员方法全部都是继承而来的。

    4、TreeSet类

    从图中可以看出:
    TreeSet继承于AbstractSet,并且实现了NavigableSet接口。
    TreeSet的本质是一个"有序的,并且没有重复元素"的集合,它是通过TreeMap实现的(见构造方法)。TreeSet中含有一个"NavigableMap类型的成员变量"m,而m实际上是"TreeMap的实例"。

    4.1成员变量

    4.2构造方法

    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }

    4.3TreeSet排序

    public static void demoOne() {
            TreeSet<Person> ts = new TreeSet<>();
            ts.add(new Person("张三", 11));
            ts.add(new Person("李四", 12));
            ts.add(new Person("王五", 15));
            ts.add(new Person("赵六", 21));
            
            System.out.println(ts);
        }
    为什么会报错

     

      

    案例String类:

     

    匿名内部类改进:

    import java.util.Comparator;
    import java.util.TreeSet;
    
    /*
     * TreeSet对元素排序的原理:
     * 1.让元素具有比较性
     * 2.集合本身具有比较性
     * 
     * 取决于创建集合对象时使用的构造方法.
     * 
     * 
     */
    
    class Student /* implements Comparable<Student> */ {
    
        private String name;
        private int 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 Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public Student() {
            super();
            // TODO Auto-generated constructor stub
        }
        /*
         * @Override public int compareTo(Student o) { // 首要条件:按照年龄比较 int r1 = -(age
         * - o.getAge()); //次要条件:名字,它已经实现了Comparable接口 int r2 = (r1 == 0)?
         * -(name.compareTo(o.getName())) : r1; return r2; }
         */
    }
    /*
    class MyComparator implements Comparator<Student> {
    
        @Override
        public int compare(Student o1, Student o2) {
            // o1--> this o2 --> other
    
            int r1 = o1.getAge() - o2.getAge();
            int r2 = (r1 == 0) ? o1.getName().compareTo(o2.getName()) : r1;
            return r2;
        }
    
    }
    */
    
    public class TreeSetDemo1 {
    
        public static void main(String[] args) {
            /*
             * // 没有传参,意味着使用元素本身的比较性. TreeSet<Student> ts = new TreeSet<Student>();
             * 
             * Student s1 = new Student("tom2", 12); Student s2 = new
             * Student("tom3", 13); Student s3 = new Student("tom2", 13); Student s4
             * = new Student("tom1", 12);
             * 
             * ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4);
             * 
             * // for (Student student : ts) { System.out.println(student.getName()
             * + "--" + student.getAge()); }
             * 
             */
    
            // 让集合具有比较性
    //        TreeSet<Student> ts = new TreeSet<>(new MyComparator());
            TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    // o1--> this o2 --> other
    
                    int r1 = o1.getAge() - o2.getAge();
                    int r2 = (r1 == 0) ? o1.getName().compareTo(o2.getName()) : r1;
                    return r2;
                }
                
            });
            
    
            Student s1 = new Student("tom2", 12);
            Student s2 = new Student("tom3", 13);
            Student s3 = new Student("tom2", 13);
            Student s4 = new Student("tom1", 12);
    
            ts.add(s1);
            ts.add(s2);
            ts.add(s3);
            ts.add(s4);
    
            for (Student s : ts) {
                System.out.println(s.getName() + "--" + s.getAge());
            }
    
        }
    
    }
    上面两个案例的代码

     练习:

     从键盘上录入3个学生的信息,包括语文,数学,英语的成绩三个成员变量,并根据总成绩进行排序

    public class Student implements Comparable<Student>{
        private String name;
        private int ch;
        private int math;
        private int en;
        
        public Student(String name, int ch, int math, int en) {
            super();
            this.name = name;
            this.ch = ch;
            this.math = math;
            this.en = en;
        }
        public Student() {
            super();
            // TODO Auto-generated constructor stub
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getCh() {
            return ch;
        }
        public void setCh(int ch) {
            this.ch = ch;
        }
        public int getMath() {
            return math;
        }
        public void setMath(int math) {
            this.math = math;
        }
        public int getEn() {
            return en;
        }
        public void setEn(int en) {
            this.en = en;
        }
        
        public int getSum() {
            return ch + math + en;
        }
        
        @Override
        public int compareTo(Student s) {
            //首要条件
            int r1 = getSum() - s.getSum();
            //次要条件:语文成绩:
            int r2 = (r1 == 0)?getCh()-s.getCh():r1;
            //次次要条件:数学成绩:
            int r3 = (r2 == 0)?getMath() - s.getMath():r2;
            //次要条件:名字
            int r4 = (r3 == 0)?getName().compareTo(s.getName()):r3;
            return r4;
        }
        
        
    }
    Student 类
    import java.util.Scanner;
    import java.util.TreeSet;
    public class Test{
    
        public static void main(String[] args) {
            TreeSet<Student> set = new TreeSet<Student>();
            
            Scanner sc = new Scanner(System.in);
            for(int i = 1;i<4;i++){
                System.out.print("输入第" + i + "个学生姓名 : ");
                String name = sc.next();
                System.out.print("输入第" + i + "个学生语文成绩 : ");
                int ch = sc.nextInt();
                System.out.print("输入第" + i + "个学生数学成绩 : ");
                int math = sc.nextInt();
                System.out.print("输入第" + i + "个学生英语成绩 : ");
                int en = sc.nextInt();
                
                Student stu = new Student(name, ch, math, en);
                
                set.add(stu);
            }
            
            //
            for (Student s : set) {
                System.out.println(s.getSum() +","+ s.getName() +","+ s.getCh() +","+ s.getMath() +","+ s.getEn());
            }
    
        }
    
    }
    Test

    4.4顺序遍历

    Iterator顺序遍历

    for(Iterator iter = set.iterator(); iter.hasNext(); ) { 
        iter.next();
    }   

    Iterator顺序遍历

    // 假设set是TreeSet对象
    for(Iterator iter = set.descendingIterator(); iter.hasNext(); ) { 
        iter.next();
    }

    for-each遍历HashSet

    // 假设set是TreeSet对象,并且set中元素是String类型
    String[] arr = (String[])set.toArray(new String[0]);
    for (String str:arr)
        System.out.printf("for each : %s
    ", str);

    TreeSet不支持快速随机遍历,只能通过迭代器进行遍历!

    4.5 方法的源码研究

    以后写吧

  • 相关阅读:
    516. Longest Palindromic Subsequence
    31. Next Permutation
    572. Subtree of Another Tree
    vue中无法监听到子组件的事件的问题
    vue中注意watch的执行顺序
    vue中路由懒加载浅析
    关于typescript 报错问题
    深入理解Typescript中文版
    vue中的高阶组件
    vue中的mixins
  • 原文地址:https://www.cnblogs.com/wqbin/p/11192280.html
Copyright © 2011-2022 走看看