JAVA_基础集合List接口与Set接口(二)

List接口

JDK API中List接口的实现类常用的有:ArrayListLinkedListVector

ArrayList:作为List接口的主要实现类;线程不安全;底层使用Object[] elementData储存。
LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表储存。
Vector:作为List接口的古老实现类;线程安全的;效率底;底层使用Object[] elementData储存。

ArrayList的源码分析:JDK 7
ArrayList list = new ArrayList();(底层创建了长度是10的Object[]数组elementData
list.add(123);elementData[0] = new Integer(123);
list.add(11);(如果此次的添加导致底层elementData数组容量不够,则扩容。)
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。

结论:建议在开发过程中使用带参数的构造器:ArrayList list = new ArrayList(int capacity)

ArrayList的源码分析:JDK 8

List接口方法

void add(int index, Object ele):在index位置插入ele元素 。
boolean addAll(int index, Collection eles):从index位置开始将eles中 的所有元素添加进来 。
Object get(int index):获取指定index位置的元素 。
int indexOf(Object obj):返回obj在集合中首次出现的位置 。
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 。
Object remove(int index):移除指定index位置的元素,并返回此元素 。
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex):返回从fromIndextoIndex 位置的子集合。

public void test() {
        ArrayList list = new ArrayList();
        list.add(132);
        list.add(456);
        list.add("AA");
        list.add(new Person("Tom", 12));
        System.out.println(list);// [132, 456, AA, Person{name='Tom', age=12}]
        list.add(1,"BB");
        System.out.println(list);// [132, BB, 456, AA, Person{name='Tom', age=12}]
        List list1 = Arrays.asList(1, 2, 3);
        list.addAll(list1);
        System.out.println(list.size());// 8
        System.out.println(list.get(1));// BBjava
    }
public void test1() {
        ArrayList list = new ArrayList();
        list.add(132);
        list.add(456);
        list.add("AA");
        list.add(new Person("Tom", 12));
        list.add("456");
        int i = list.indexOf(456);
        System.out.println(i);// 1
        System.out.println(list.lastIndexOf("AA"));// 2
        Object remove = list.remove(0);
        System.out.println(remove);// 132
        System.out.println(list);// [456, AA, Person{name='Tom', age=12}]
        Object cc = list.set(1, "CC");
        System.out.println(cc);// AA
        System.out.println(list);// [456, CC, Person{name='Tom', age=12}]
        List subList = list.subList(2, 4);
        System.out.println(subList);// [Person{name='Tom', age=12}, 456]
        System.out.println(list);// [456, CC, Person{name='Tom', age=12}, 456]
    }
public void test3() {
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");
        list.add("BB");
        // 方式一:Iterator迭代器方式循环遍历
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        System.out.println("*******************");
        // 方式二:forEach增强循环遍历
        for (Object obj : list) {
            System.out.println(obj);
        }
        System.out.println("*******************");
        // 方式三:普通for循环遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

Set接口

Set接口:储存无序的、不可重复的数据。
HashSet:作为Set接口的主要实现类;线程不安全的;可以储存null值。
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet:可以按照添加对象的指定属性,进行排序。

一、储存无序、不可重复:(以HashSet为例说明)

  1. Set无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加。而是根据数据的哈希值决定的。
  2. Set不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。

二、添加元素的过程:(以HashSet为例说明)

  1. 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法出生在HashSet底层数组中的位置(即为:索引位置)。
    数组此位置上是否已经有元素:
    如果有此位置上没有其它元素,则元素a添加成功。→情况1
    如果此位置上有其它元素B(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
    如果hash值不相同,则元素a添加成功。→情况2
    如果hash值相同,进而需要调用元素a所在类的equlas()方法:
    equlas()返回true,元素a添加失败。
    equlas()返回false,则元素a添加成功。→情况3
    对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式储存。
    JDK 7:元素a放到数组中,指向原来的元素。
    JDK 8:原来的元素在数组中,指向元素a。
    总结:七上八下

    HashSet底层:数组+链表的结构。

    要求:向Set中添加的数据,其所在的类一定要重写hashCode()equlas()
    要求:重写的hashCode()equlas()尽可能的保持一致性:相等的对象必须具有相等的散列码(hash值)。

    public void test() {
            Set set = new HashSet();
            set.add(456);
            set.add(123);
            set.add("AA");
            set.add("CC");
            set.add(new User("Tom",20));
            set.add(new User("Tom",20));
            set.add(new User("Jerry",19));
            set.add("DD");
            Iterator iterator = set.iterator();
            while(iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        }
    
    public class User {
        private String name;
        private int age;
    	...
        @Override
        public String toString() {
    		...
        }
        @Override
        public boolean equals(Object o) {
            System.out.println("User equals()...");
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            User user = (User) o;
    
            if (age != user.age) return false;
            return name != null ? name.equals(user.name) : user.name == null;
        }
        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
        }
    }
    

三、LinkedHashSet的使用

LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
​ 优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

    public void test2() {
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",20));
        set.add(new User("Tom",20));
        set.add(new User("Jerry",19));
        set.add("DD");

        Iterator iterator = set.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

四、TreeSet的使用

  1. TreeSet中添加的数据,要求是相同类的对象。
    失败:不能添加不同类的对象

    set.add(123);
    set.add("AA");
    set.add(new User("Tom",20));
    
  2. 两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator

    自然排序中,比较两个对象是否行同的标准为:compareTo()返回0,不再是equlas()

    public void test1() {
            TreeSet set = new TreeSet();
            // 举例一:
    //        set.add(123);
    //        set.add(456);
    //        set.add(-487);
    //        set.add(9);
            // 举例二:
    //        set.add("AA");
    //        set.add("DD");
    //        set.add("BB");
    //        set.add("ABC");
            // 举例三:
            set.add(new User("Tom",22));
            set.add(new User("Jerry",22));
            set.add(new User("Jim",20));
            set.add(new User("Mike",54));
            set.add(new User("Jack",58));
            set.add(new User("Jack",20));
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        }
    
    public class User implements Comparable {
        private String name;
        private int age;
        @Override
        public String toString() {...}
        @Override
        public boolean equals(Object o) {...}
        @Override
        public int hashCode() {...}
        // 按照姓名从小到大排序,年龄从小到大排序
        @Override
        public int compareTo(Object o) {
            if (o instanceof User) {
                User user = (User) o;
                int compare = user.name.compareTo(this.name);
                if (compare != 0) {
                    return compare;
                }else {
                    return Integer.compare(this.age,user.age);
                }
            }else {
                throw new RuntimeException("当前传递的类型不匹配");
            }
        }
    }
    

    定制排序中,比较两个对象是否行同的标准为:compare()返回0,不再是equlas()

    public void test2() {
            Comparator comparator = new Comparator() {
                // 按照年龄从小到大排序
                @Override
                public int compare(Object o1, Object o2) {
                    if (o1 instanceof User && o2 instanceof User) {
                        User user1 = (User) o1;
                        User user2 = (User) o2;
                        return Integer.compare(user1.getAge(),user2.getAge());
                    }else {
                        throw new RuntimeException("传入的类型不匹配");
                    }
                }
            };
            TreeSet set = new TreeSet(comparator);
            set.add(new User("Tom",22));
            set.add(new User("Jerry",21));
            set.add(new User("Jim",20));
            set.add(new User("Mike",54));
            set.add(new User("Mary",54));
            set.add(new User("Jack",58));
            set.add(new User("Jack",20));
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        }
    
posted @ 2021-01-04 09:47  TuoYingtao  阅读(76)  评论(0编辑  收藏  举报