黑马程序员技术交流社区

标题: 【广州校区】【原创】集合的总结与常见问题解析 [打印本页]

作者: 帅气de路人甲    时间: 2018-10-18 09:47
标题: 【广州校区】【原创】集合的总结与常见问题解析
1、如何定义Collection?
        Collection是一个集合体系的顶层接口,此接口中的方法都是:public abstract。
        可以进行增删改查的操作:
                增加:   -->>   boolean add(Object o)                        一次添加一个元素,元素可以是Object 的子类,返回true
                                boolean addAll(Collection c)                一次添加一批元素,参数是Collection类型的
                删除:   -->>   void clear()                                 清空集合
                                boolean remove(Object o)                一次删除一个元素,成功删除,返回true
                                boolean removeAll(Collection c)                一次删除一批,成功,返回true
                查找:   -->>   boolean contains(Obejct o)                判断集合中是否包含指定元素
                                boolean containsAll(Collection c)        是否包含一批
                                boolean isEmpty()                        判断集合是否为空
                                int size()                                获取集合中的个数
                修改:   -->>   clear()                                        清空集合中的所有元素
                获取:   -->>   int size()                                返回集合容器的大小
                集合转换为数组:
                                Object[] toArray();        该方法将集合转为数组,集合中的每一个元素,作为了数组一个的元素
【注意:集合中存储的是对象的引用,而不是对象本身】
【注意:接口与接口之间属于:继承关系】

2、录入用户在控制台输入的信息,可以使用Scanner类
        Scanner sc = new Scanner();

3、List接口:  ---->>   public interface List extends Collection
        List是一个继承了Collection接口的接口,具备了比Collection多的功能。
        特点: ---->>   有序、有角标、可重复。
                        此接口的用户可以对List接口中每个元素的插入位置进行精确地控制。(通过角标获取集合中的元素)
       
        增:
                void add(int index,Object element)        指定元素添加的角标,增加一个元素
                boolean addAll(int index,Collection c)        指定位置,增加一批
        删:
                Element remove(int index)                        删除指定位置的元素,并且返回该元素
        改:
                Element set(int index,Object element)        替换指定位置的元素,需要确定该元素的位置和新元素,返回就元素。
                List<E> subList(int fromIndex,int toIndex)        截取子结合,返回一个List,包含头,不包含尾。
        查:
                Object get(int index)                        获取List结合指定位置的元素
                int indexOf(Object o)                        返回此列表中第一次出现的指定元素的索引
                int lastIndexOf(Object o)                倒序查找指定元素的位置
                        【注意角标越界:IndexOutOfBoundException  //  找不到返回:-1】
                        【Object set = new list.get(list.size()-1);】
        【List集合特有的增删改查方法都跟角标有关系!!!!】
【问:如何取出List集合中的元素?】
        通过for循环,并使用size()和set()方法可取出List集合中的每一个元素。

4、ArrayList  -->>  是一个List接口的实现类
                    具备List集合的特点:有序、有角标、可重复。
                        该类内部,维护了一个数组, 数组的元素是Object 类.
                        在ArrayList 类的构造函数中初始化的该数组, 如果没有显示的指定数组的长度,【默认长度是10】
                                也就是说: 使用无参数构造,new ArraList(), 自动创建的数组长度是60%.
                        ArrayList 在添加元素时,都会检测数组是否已满,如果满了,自动扩容(创建一个新数组),
                                【新数组长度的是老数组的1.6倍】
                                并且将老数组中的元素拷贝到了新数组,使用新数组继续增加元素.

5、ArrayList使用什么实现的?其优缺点是什么?
        ArrayList是使用数组实现的。
        优点:内存地址连续,查找快;
        缺点:增加和删除元素,需要设计数组的扩容或者拷贝,效率很低。
        ---->>   总结:  数组实现,查找快,增删慢。
        ArrayList中的方法:
            boolean contains(Object o)    判断集合是否包含指定的元素,涉及了元素的比较(对象的比较)
                                          内部使用了元素的equals()方法。
            String类                          重写了equals()方法

6、LinkedList  ---->>  List接口的实现类,具备:有序、有角标、元素可重复的特点。【和ArrayList类似】
        LinkedList是一个双向链表,该集合提供了方便操作集合头和集合尾的方法。
        【如果集合中没有元素可以获取或者删除,则抛:NoSuchElementException】
        LinkedList特有的方法:
                增加:
                        void addFirst(Element e)
                        void addLast(Element e)
                删除:
                        Object getFirst()
                        Object getLast()
                查找:
                        Object removeFirst()                移除并返回此列表的第一个元素
                        Object removeLast()                移除并返回此列表的最后一个元素
        数据结构:
                堆栈:
                        void push(Element e)                将元素推入此列表所表示的堆栈
                        Element pop()                        从此列表所表示的堆栈处弹出一个元素
                       【压栈和弹栈:先进后出,后进先出】
                队列:
                        boolean offer(Element e)        将指定元素添加到此列表的末尾(最后一个元素)
                        Element poll()                        获取并移除此列表的头(第一个元素)
                        Element peek()                        获取但不移除列表的头(第一个元素)
                       【队列的数据结构:先进先出】
        返回逆序的迭代器对象:
                        descendingIterator()                返回逆序的迭代器对象

7、LinkedList的实现原理是什么?其优缺点?
        LinkedList的实现原理是:链表实现,其内存地址是不连续的。
        优点:相对于数组,增加元素快;
        缺点;由于内存地址不连续,查找性能低。

8、集合与数组有什么区别?
        相同点:数组和集合都是容器
                数组和集合中存放的都是【对象的引用】而非对象本身
        不同点:数组存储【基本数据类型】,是单一的。而且一旦声明好长度后,长度不可变;
                集合【只能】储存【对象】,但是可以是任意类型的对象,其长度可变。

【集合的分类】
    ----|Iterable:接口
        Iterator iterator()
        ----|Collection:单列集合
                ----|List:        有序存储顺序,可重复
                        ----|ArrayList:        数组实现,查找快、增删慢
                                                由于是数组实现,在增和删的时候会牵扯到数组增容,以及拷贝元素,所以慢;
                                                数组是可以直接按索引查找的,所以查找时比较快。
                        ----|LinkedList:        链表实现,增删快、查找慢
                                                由于链表实现,增加时只要让前一个元素记住自己就可以了,删除时让前一个元
                                                素记住后一个元素,后一个元素记住前一个元素,这样的增删效率高;
                                                但查询时需要一个一个的遍历,所以效率比较低。
                        ----|Vector:                多线程安全、效率略低        【ArrayList单线程效率高,但是多线程要使用Vector】
                ----|set:        无序存储,不可重复
                        ----|HashSet                线程不安全,存取速度快
                                                底层是以hash表实现的
                        ----|TreeSet                红-黑树的数据结构,默认对元素进行自然排序(String)
                        【TreeSet自身具备排序功能】
                                ----|Comparable
                                        ----|compareTo(Object o)            元素自身具备比较性
                                ----|Comparator
                                        ----|compare(Object o1,Object o2)    给容器传入比较器
                                                如果在比较的时候两个对象返回值是【0】,那么这两个元素【重复】
                                【当Comparable和Comparator比较方式同时存在时,以Comparator比较方式为主】
                        ----|LinkedHashSet        会保存插入的顺序
    ----|Map:  将键映射到值的对象。一个映射不能包含重复的键,每个键最多只能映射一个值。
                interface Map<K,V>       
        ----|TreeMap                底层是二叉树数据结构,可以对map集合中的键进行排序
                                需要使用Comparable或者Comparator进行比较排序。
                                        【return 0 判断键的唯一性】
        ----|HashTable                底层是哈希表数据结构,线程是【同步】的  -->>  不可以存入null键、null值
                                效率较低,故被【HashMap】替代
        ----|HashMap                采用哈希表实现  -->>  【无序】
                                底层是哈希表数据结构,线程是【不同步】的  -->>  可以存入null键、null值
                【要保证键的唯一性,需要覆盖hashCode()方法和equals()方法】
        ----|LinkedHashMap

        【常用方法:】
                添加:        V put(K key,V value)        可以是相同的key值,但是添加的value值会覆盖前面的
                        putAll(Map<? extends K , ? extends V> m)
                                                从指定映射中将所有映射关系复制到此映射中(可选操作)
                删除:        remove(Object key)        删除关联对象,指定key对象
                        clear()                        清空集合对象
                获取:        value get(Object key)        可以用于判断键是否存在的情况。
                判断:        boolean isEmpty()        如果此映射不包含键-值映射关系【即长度为0】,则返回true,否则返回false
                        boolean containsKey(Object key)
                                                判断集合中是否包含指定的key
                        boolean containsValue(Object value)
                                                判断集合中是否包含指定的value
                                                当指定的键不存在的时候,返回的是null
                长度:        int size()                返回此映射中的键-值映射关系数

9、在什么时候该使用什么样的集合?
        Collection        当我们需要保存若干个对象的时候使用集合
          -->>  List        如果需要保留存储顺序、并且重复元素时,使用List
                  -->>          如果查询较多,使用ArrayList;
                          如果存取较多,使用LinkedList;
                          如果需要线程安全,使用Vector。
          -->>  Set        如果不需要保留存储顺序,并且要去掉重复元素时,使用Set
                  -->>          如果需要将元素排序,使用TreeSet;
                          如果不需要排序,使用HashSet        【HashSet比TreeSet效率高】
                          如果需要保留存储顺序,同时要过滤重复元素,使用LinkedHashSet。

10、自定义对象时为什么要重写toString()和equals()方法?
        因为Object是自定义类的父类,Object类中的toString()方法返回的是哈希值;
        Object类中的equals()方法比较的是对象的地址值。

【去除集合中重复的元素】
    代码如下:
    public class Demo{
        public static void main(String[] args){
            ArrayList arr = new ArrayList();
            Person p1 = new Person("jack",20);
            Person p2 = new Person("rose",18);
            Person p3 = new Person("rose",18);
            arr.add(p1);
            arr.add(p2);
            arr.add(p3);
            System.out.println(arr);
            ArrayList arr2 = new ArrayList();
            for(int i=0;i<arr.size();i++){
                Object obj = arr.get(i);
                Person p = (Person)obj;
                if(!arr2.contains(p)){
                    arr2.add(p);
                }
            }
            System.out.println(arr2);
        }
    }
    class Person{
        private String name;
        private int age;
        public Person(){
       
        }
        public Person(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;
        }
        public int hashcode(){
            return this.name.hashCode() + age*37;
        }
        public boolean equals(Object obj){
            if(!(obj instanceof Person)){
                return false;
            }
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age = p.age;
        }
        public String toString(){
            return "name:" + this.name + "age:" + this.age;
        }
    }

11、Vector:  多线程安全、但是效率低    ---->>    描述的是一个线程安全的ArrayList。
        特有的方法:
                void addElement(E obj)        在集合末尾添加元素
                E elementAt(int index)        返回指定角标的元素
                Enumeration element()        返回集合中的所有元素,封装到Enumeration对象中
        Enumeration接口:
                boolean hasMoreElements()        测试此枚举是否包含更多的元素
                E nextElement()                        如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素
        【代码如下:】
        public static void main(String[] args){
            Vector v = new Vector();
            v.addElement("aaa");
            v.addElement("bbb");
            v.addElement("ccc");
            // System.out.println(v.elementAt(2));
            Enumeration ens = v.elements();
            while(ens.hasMorreElements()){
                System.out.println(ens.nextElement());
            }
        }

12、Iterable:        是Collection的父接口,实现Iterable的类可以进行迭代,并且支持增强for循环
                该接口只有一个方法,用于返回集合迭代器对象!                【获取迭代器的方法iterator()】
                piblic interface Iterable<T>
        Iterator<T> iterator():该类主要用于遍历集合对象,并描述了遍历集合的常见方法
                boolean hasNext()        判断集合中是否有元素,如果有元素可以迭代,就返回true。
                E next()                返回迭代的下一个元素。
                                        如果没有下一个元素,调用next()会抛出  -->>  NoSuchElementException
                void remove()                从迭代器指向的集合中移除迭代器返回的最后一个元素

【Iterator的for循环、清空】
        public class Demo{
            ArrayList list = new ArrayList();
                // 增加:add() 将指定对象存储到容器中
            list.add("计算机网络");
            list.add("现代操作系统");
            list.add("java编程思想");
            list.add("java核心技术");
            list.add("java语言程序设计");
            System.out.println(list);
            for(Iterator it = list.iterator();it.hasNext();){
                // 迭代器的next()方法返回值类型是Object,所有要记得【类型强转】
                String next = (String)it.next();
                System.out.println(next);
                it.remove();
            }
        }
【细节一:如果迭代器的指针已经指向了集合的末尾,那么如果再调用next()会返回NoSuchElementException异常】
【细节二:如果调用remove()之前没有调用next()方法是不合法的,会抛出IllegalStateException异常】
【细节三:当一个集合在循环中即使用引用变量操作集合,又使用迭代器操作集合对象,会抛出ConcurrentModificationException异常】

13、为什么next()方法的返回值类型是Object呢?
        为了可以接收任意类型的对象
        如果返回的时候不知道是什么类型的,就定义为object

14、Iterator和ListIterator有什么关系?
        ListIterator是Iterator的子接口,是List集合特有的迭代器。
        Iterator在迭代时,只能对元素进行获取【next()】和删除【remove()】的操作;
        ListIterator在迭代list集合时,还可以对元素进行添加【add(obj)】和修改【set(obj)】的操作。

15、List集合特有的迭代器ListIterator
                ---->>  public interface ListIterator extends Iterator
    ListIterator<E>   listIteraotr()
    ----| Iterator
                hasNext()
                next()
                remove()
        ----| ListIterator
                    add(E e)                将指定的元素插入列表(可选操作)。
                                        该元素直接插入到next()返回的下一个元素的前面(如果有)
                    void set(E o)        用指定的元素替换next()或previous()返回的 【最后】 一个元素
                    hasPrevious()        逆向遍历列表,列表迭代器有多个元素,则返回true
                    previous()                返回列表中的前一个元素

16、HashSet是如何判断两个元素重复的?
        通过hashCode()方法和equals()方法来保证元素的唯一性,add()方法返回的是boolean类型
        【调用原理:HashSet集合在判断元素是否相同,先判断hashCode()方法,相同才会判断equals()方法;不相同不会调用equals()】

17、HashSet和ArrayList集合在判断元素时是否有相同的方法?
        有:boolean contains(Object o)
        HashSet使用hashCode()和equals()方法,ArrayList使用eqauls()方法。

18、给TreeSet指定排序规则:
        方式一:【元素自身】具备比较性
                  元素自身具备比较性,需要元素实现【Comparable接口】,重写【compareTo方法】,
                也就是让元素自身具备比较性,这种方式叫做元素的【自然排序】也叫做【默认排序】。
        方式二:【容器】具备比较性
                 当元素自身不具备比较性,或者自身具备的比较性不是所需要的。
                那么此时可以让容器自身具备。需要定义一个类实现【Comparator接口】,重写【compare方法】,
                并将该接口的子类实例对象作为参数传递给【TreeSet集合】的【构造方法】。
        注意:当Comparable比较方式和Comparator比较方式同时存在时,以【Comparator】的比较方式为主;
        注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。
                通过return 0 来判断唯一性。

19、为什么使用TreeSet存入字符串,字符串默认输出是按升序排列的?
        因为字符串实现了一个接口,叫做【Comparable接口】,字符串重写了该接口的【compareTo()方法】,
        所以String对象具备了比较性。
        【自定义的元素(比如Person类、Book类)想要存入TreeSet集合,就必须实现Comparable接口,也就是要让自定义对象具备比较性】
        【存入TreeSet集合的元素都要具备比较性:要实现Comparable接口、并重写该接口的compareTo()方法】

20、总结:
        看到array,就要想到角标。
        看到link, 就要想到first,last。
        看到hash, 就要想到hashCode,equals.
        看到tree, 就要想到两个接口。Comparable,Comparator。

21、TreeSet是如何保证元素的唯一性的?
        通过【compareTo】或者【compare】方法来保证元素的唯一性。
        当Comparable接口中的compareTo()函数返回值为【0】时,说明两个对象相等,此时该对象不会被添加进来。

22、使用TreeSet集合将字符串 String str = "8 10 15 5 2 7"; 的数值进行排序。
        public class Demo{
            public static void main(String[] args){
                String str = "8 10 15 5 2 7";
                String strs = str.split(" ");
                TreeSet ts = new TreeSet();
                for(int x= 0;x<strs.length();x++){
                    int y = Integer.parseInt(strs[x]);
                    ts.add(y);
                }
                System.out.println(ts);
            }
        }

23、遍历Map集合的方式有哪些?
        方式一:使用keySet
                        将Map转成Set集合【keySet()】,通过Set的迭代器【Iterator】取出Set集合中的每一个元素,
                        即Map集合中所有的键,再通过get()方法获取键对应的值
                                Set<Integer> ks = map.keySet();
                                Iterator<Integer> it = ks.iterator();
                                while(it.hasNext()){
                                    Integer key = it.next();
                                    String value = map.get(key);
                                }
        方式二:通过values获取所有值,但是不能获取到key对象
                                Collection<String> vs = map.values();
                                Iterator<String> it = vs.iterator();
                                while(it.hasNext()){
                                    String value = it.next();
                                }
        方式三:Map.Entry        -->>        public static interface Map.Entry<K,V>
                        通过Map中的entrySet()方法获取存放Map.Entry<K,V>对象的Set集合        -->>        Set<Map.Entry<K,V>> entrySet()
                                Set<Map.Entry<Integer,String>> entrySet = map.entrySet();
                                Iterator<Map.Entry<Integer,String>> it = entrySet.iterator();
                                while(it.hasNext()){
                                    Map.Entry<Integer,String> en = it.next();
                                }
                                Integer key = en.getKey();
                                String value = en.getValue();







欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2