Fork me on GitHub

集合

注意:所有文章除特别说明外,转载请注明出处.

集合

[TOC]

注意:Object类型的数组可以存储任意类型的数据。

1.集合

集合是存储对象数据的容器。

1.集合比数组的优势:

1.集合可以存储任意类型的数据,数组只能存储同一种数据类型的数据(Object类型的数组除外)。
2.集合的长度是会发生变化的,数组的长度是固定的。

2.集合类的继承体系:

Collection:是所有单例集合的根接口,其有两个接口List/Set
Set:如果实现了Set接口的集合类,具备特点:无序、不可重复。
List:如果实现了List接口的集合类,具备的特点:有序、可重复。有序:集合的有序不是指自然顺序,而是指添加进去的顺序与元素出来的顺序一致。

提示:Collection与Collections的区别在于Collection是一个集合接口,其提供了对集合对象进行操作的通用接口方法。实现接口类主要有List/Set,该接口的设计是为各种具体的集合提供最大化的统一操作方式。Collections是针对集合类的一个包装类,它提供了一系列静态方法以实现对各种集合的搜索、排序、线程安全化等操作。

继承:1.父类定义完整的成员、静态成员、非静态成员、构造方法,静态变量和静态方法都可以通过子类名.父类静态成员的形式调用。2.所有的私有成员不能继承private修饰的成员。3.构造函数不能被继承。

super关键字

this关键字和super关键字很像,this关键字指向的是当前对象的调用,super关键字指向的是当前对象调用的父类。this/super关键字只能在有对象的前提下使用,不能在静态上下文使用。

提示:子类的构造函数默认第一行会调用父类的无参构造函数。super();

提示:在子类构造函数第一行通过super关键字调用父类任何构造函数,如果显式调用父类的构造函数,编译器自动添加的调用父类无参数的构造就消失,构造函数的调用只能放在第一行,只能调用一次。super()和this()不能同时存在构造函数的第一行。


Collection接口中的方法(因为接口Collection不可以直接实现,此时使用多态ArrayList):

增加:<br>
   1.add(E e):添加成功返回true,添加失败返回false<br>
   2.addAll(Collection c):把一个集合中的元素添加到另外一个集合中去。例:c2.add("王林");c.addAll(c2);//就是将c2的元素添加到c集合中去。

删除:<br>
   1.clear():清空集合中的元素<br>
   2.remove(o):指定集合中的元素删除,删除成功返回true,删除失败返回false<br>
   3.removeAll():该方法是删除一个集合中与另一个集合的交集元素。c.removeAll(c2);//删除c集合中与c2集合中的交集元素,因为是c集合调用此方法。<br>
   4.retainAll():保留c集合与c2的交集元素,其它的元素一并删除。c.retainAll(c2);//


查看:<br>
   size():查看集合中元素的个数。c.size();//查看c集合中元素的个数

判断:<br>
   1.isEmpty():该方法是判断集合是否为空<br>
   2.contains(Object o):判断集合中是否存在指定的元素<br>
   3.containsAll(Collection c):判断集合c中是否包含c2的所有元素。c.containsAll(c2);//

迭代:<br>
 遍历集合中的元素的方法:<br>
    方式一:使用toArray()方法 将c集合中的所有元素存储到一个Object数组中返回。<br>
    方式二:使用迭代器iterator()遍历,Iterator it=c.iterator();//返回一个迭代器<br>
   1.toArray():将集合中的元素全部存储到一个Object数组中返回。

   2.iterator():

迭代器的作用:用于抓取集合中的元素,就类似与抓娃娃机里面的那个爪子一样。iterator实际上返回的是iterator接口的实现类对象。

迭代器的方法:<br>
    1.hasNext():问是否存在下一个元素,如果有元素则返回true,否则返回false。(当前指针是否有指向元素,如果有返回true,否则返回false)<br>
    2.next():获取当前指针指向的元素并返回当前的元素,然后指针向下移动一个单位。<br>
    3.remove():删除迭代器最后一次返回的元素。

注意:从Object数组中取出的元素只能使用Object类型声明变量接收,如果需要其它的变量类型,需要强制转换。

2.List:

List继承Collection接口,所以Collection中有的方法,在List接口中也适用。

List接口中特有的方法:

1.添加:
    add(int index,E element):将指定元素添加到集合中的指定索引值位置上。
    addAll(int index,Collection c):将指定集合添加到另一集合中指定索引值的位置上。

2.获取:
    get(int index):使用get方法遍历集合中的元素,即获取指定索引值位置的集合中的元素
    indexOf(Object o):找出指定元素第一次出现在集合中的索引值
    lastIndexOf(Object o):找出指定元素最后一次出现在集合中的索引值
    subList(fromIndex,toIndex):指定开始与结束的索引值,截取集合中的元素(在Java中截取都是包头不包尾的,如果想要截取到指定长度的元素,应该将末尾值加1)

3.修改:
    set(int index,E element):使用指定的元素替换指定索引值的元素

注意:List接口中特有的方法具备的特点:操作的方法都存在索引值,同时只有List接口下面的集合类才具备索引值。其它接口类下面的集合类都没有索引值。

4.迭代:
    listIterator():该方法返回的是一个List接口中特有的迭代器

ListIterator接口中特有的方法:
    hasPrevious():判断是否存在上一个元素
    previous():获取上一个元素,即当前指针先向上移动一个单位,然后再取出当前指针指向的元素

    add(E e):将当前元素插入到当前指针指向的位置上
    set(E e):替代迭代器最后一次返回的元素

练习:使用三种方式遍历集合的元素

1.使用get方法遍历
2.使用迭代器正序遍历
3.使用迭代器逆序遍历

package cn.itcast.collelction;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class demo4 {
    public static void main(String[] args){
        List list=new ArrayList();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        System.out.println("=======使用get方法遍历======");
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i)+",");
        }
        System.out.println("========使用迭代其正序遍历======");
        ListIterator it=list.listIterator();//获取迭代器
        while(it.hasNext()){
            System.out.println(it.next()+",");
        }
        System.out.println("=========使用迭代逆序遍历=======");
        while(it.hasPrevious()){//hasPrevious():判断是否存在上一个元素
            System.out.println(it.previous()+",");//previous():获取上一个元素,即当前指针先向上移动一个单位,然后再取出当前指针指向的元素
        }

    }

}

迭代器:指专门取出集合元素的对象,但是此对象很特殊,不能直接创建对象(new),该对象是以内部类的形式存在每个集合类的内部。因为每个容器都有取出元素的功能。即Iterator是一个对象,它的工作是遍历并选择序列中的对象,它提供一种访问一个容器(container)对象中的各个元素,而又不必暴露该对象内部细节的方法。所以通过迭代器,开发人员不需要了解容器底层的接口,就可以实现对容器的遍历。

迭代器在遍历元素时注意事项:
1.在迭代器(内存模型)迭代元素的过程中,不允许使用集合对象改变集合中的元素的个数,如果需要添加删除只能使用迭代器的方法进行操作。如果使用了集合对象改变集合中元素个数就会出现ConcurrentModificationException异常。

提示:1.使用容器iterator()方法返回一个Iterator,然后通过Iterator的next()方法返回第一个元素。2.使用Iterator的hasNext()方法判断容器中是否还有元素,如果有,可以使用next()方法获取下一个元素。3.可以通过remove()方法删除迭代器返回的元素。

3.List的实现类

1.ArrayList
    ArrayList特有的方法:
     1.ensureCapacity(int minCapacity) 该操作增加ArrayList容量
        2.trimToSize()
ArrayList底层维护了一个Object数组实现的(底层是Objec数组写的),所谓动态的是它的大小可变。 特点:查询速度块,增删慢(因为此时如果ArrayList长度不够用的时候需要创建新的ArrayList对象,然后将原来的数据拷贝到新建的对象中,那么这一过程就非常的漫长)

ArrayList何时使用:根据数据特点,如果当前数据查询比较多,增删比较少的时候,那么就使用ArrayList存储这批数据。比如高校的图书馆,这时候查询的操作比较多,然后增删的操作比较少,此时可以使用ArrayList来操作。

笔试题目:使用ArrayList无参构造函数创建一个对象时,默认的容量是多少?如果长度不够使用时又自增增长多少?

答:ArrayList底层是维护了一个Object数组实现的,使用无参构造函数时,Object数组默认的容量是10,当长度不够用时,自动增长0.5倍。

2.LinkedList
实现原理:使用链表数据结构实现的。特点:查询速度慢,增删快(因为内存地址不连续,所以查询的时候需要遍历集合完之后才能查询到相应的元素的位置)
LinkedList的特有方法:
    addFirst():将元素添加到集合的首位置上
    addLast():将元素添加到集合的末尾处
    getFirst():获取集合中首位置的元素
    getLast():获取集合中末尾的元素
    removeFirst():删除集合中的首位置元素并返回
    removeLast():删除集合中的末尾位置元素并返回

    descendingIterator():

    push():进栈
    pop():出栈

    offer():进队列
    poll():出队列

机试题目:使用LinkedList实现堆栈数据结构的存储方式与队列的数据结构存储方式

3.Vector(了解) 底层是维护了一个Object的数组实现的,实现与ArrayList一样,但是Vector是线程安全的,操作效率低。(已经被ArrayList取代)

笔试题:说出ArrayList与Vector的区别?

答:相同点:ArrayList与Vector底层都是使用了Object数组实现的。
不同点:1.ArrayList是线程不同步的,操作效率高。Vector是线程同步的,操作效率低。
2.ArrayList是JDK1.2出现的,Vector是jdk1.0出现的。


ArrayList/Vector/LinkedList之间的区别:ArrayList/Vector/LinkedList类均在java.util包中,均为可伸缩数组,即可以动态改变长度的数组。ArrayList和Vector是在内存中开辟一块连续的空间来存储的,由于数据存储是连续的,所以他们支持用序号(下标)来访问元素,同时索引数据的速度比较快,但是在插入元素时需要移动容器中的元素,所以对数据的插入操作执行比较慢。ArrayList与Vector最大的区别是synchronization(同步)的使用,没有一个ArrayList的方法是同步的,而Vector的大部分方法都是直接或间接同步的,所以Vector是线程安全的,而ArrayList是线程不安全的。LinkedList是采用双向列表来实现的,对数据的索引需要从列表头开始遍历,因此用于随机访问则效率低下,但是在插入元素时不需要对数据进行移动,所以插入效率较高。此外,LinkedList是非线程安全的容器。

4.Set接口

Set接口实现的集合类特点:无序、不可重复

1.无序:元素添加的顺序与出来的顺序是不一样的 
2.不可重复:set集合里面的元素是不可重复的。

1.Set接口下面的特有方法

Set接口下面的实现类:

1.HashSet:底层是使用哈希表来支持的,特点:存取速度快。
    实现原理:往HashSet添加元素的时候HashSet会先调用元素的hashCode()方法得到元素的哈希值,然后通过元素的哈希值经过移位运算,就可以算出该元素在哈希表中的存储位置。
          情况一:如果算出元素存储的位置目前没有存储元素,那么该元素可以直接存储到该位置。
          情况二:如果算出该元素的存储位置目前已经存在有元素,那么会调用该元素的equals()方法与该位置的元素再比较一次,如果equals方法返回的是true,那么该元素与此位置上的元素是重复的,不允许添加,反之可以直接添加。

    哈希表的特点:桶式结构(一个空间里面可以装多个元素)
注意:HashCode值在默认情况下是表示的内存地址,但是在引用类的时候类会重写hashCode()方法,所以hashCode值可能会相同。

2.TreeSet:如果元素具备自然顺序特性,那么就按照元素自然顺序的特性进行排序存储

    TreeSet注意事项:
        1.往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
        2.往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素的比较规则定义在compareTo(T o)方法中。
        3.如果在比较元素的时候,compareTo()方法返回的是0,那么该元素就被视为重复元素,不允许添加(注意:TreeSet与hashCode()、equals()方法是没有任何关系的)
        4.往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类与没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个比较器。
        5.往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口,在创建TreeSet对象的时候也传入了比较器,那么是以比较器的比较规则优先使用。

自定义比较器:自定义一个类实现Comparator接口,把元素与元素之间的比较规则定义在compare()方法即可。
    格式:
    class 类名 implements Comparator{

        }

推荐使用:比较器(comparator)
TreeSet实现原理:
底层是使用红黑树(二叉树)数据结构实现的。存储规则:左小右大

注意:TreeSet是可以对字符串进行排序的,因为字符串已经实现了Comparable接口。

字符串比较规则:
情况一:可以找到不同字符,比较的就是对应位置上的字符。

情况二:找不到对应不同的字符,比较的就是字符串的长度。

注意:1.eclipse的快捷键:Ctrl+shift+/ 添加多行注释 Ctrl+shift+\ 取消多行注释

5.Map接口

Map中的元素是两个对象,一个对象作为键,一个对象作为值。键是不可以重复的,但是值可以重复。Map与Collection在集合框架中属于并列的存在。Map存储的是键值对。Map接口,表示将键映射到值的对象,一个映射不能包含重复的键,每个键最多能映射到一个值。

1.HashMap 采用哈希表实现,无序。底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。

    1.1 LinkedHashMap:该子类基于哈希表又融入了链表。可以Map集合进行增删提高效率

2.TreeMap 可以对键进行排序,底层是二叉树数据结构。可以对map集合中的键进行排序。需要使用Comparable或者Comparator 进行比较排序。return 0,来判断键的唯一性。

3.Hashtable 底层是哈希表数据结构,线程同步,不可以存入null键,null值。效率较低,被 HashMap 替代

案例:使用HashMap建立学生姓名与年龄之间的映射关系。

import java.util.HashMap;
import java.util.Map;

public class Demo {
    public static void main(String[] args) {
        //定义一个Map容器对象
        Map<String,Integer> map1 = new HashMap<String,Integer>();
        map1.put("jack", 20);
        map1.put("rose", 18);
        map1.put("lucy", 17);
        map1.put("java", 25);
        System.out.println(map1);
        //添加重复的键值(值不同),会返回集合中原有(重复键)的值
        System.out.println(map1.put("jack",30));//20

        Map<String,Integer> map2 = new HashMap<String,Integer>();
        map2.put("Aaron",25);
        map2.put("Brian",35);
        System.out.println("map2:"+map2);
        //从指定的映射中将所有映射关系复制到此映射中
        map1.putAll(map2);
        System.out.println("map1:"+map1);

        //删除
        Map<String, Integer> map1 = new HashMap<String, Integer>();
        map1.put("jack", 20);
        map1.put("rose", 18);
        map1.put("lucy", 17);
        map1.put("java", 25);
        System.out.println(map1);                
        // 指定key,返回删除的键值对映射的值。
        // remove() 该方法删除关联对象,指定key对象
        // clear() 清空集合对象
        System.out.println("value:" + map1.remove("java"));
        map1.clear();
        System.out.println("map1:" + map1);

        //获取
        Map<String, Integer> map1 = new HashMap<String, Integer>();
        map1.put("jack", 20);
        map1.put("rose", 18);
        map1.put("lucy", 17);
        map1.put("java", 25);
        System.out.println(map1);
        // V get(Object key) 通过指定的key对象获取value对象
        // int size() 获取容器的大小
        System.out.println("value:" + map1.get("jack"));
        System.out.println("map.size:" + map1.size());

        //判断
        Map<String, Integer> map1 = new HashMap<String, Integer>();
        map1.put("jack", 20);
        map1.put("rose", 18);
        map1.put("lucy", 17);
        map1.put("java", 25);
        System.out.println(map1);
        System.out.println("isEmpty:" + map1.isEmpty());//长度为0返回true,否则返回false
        System.out.println("containskey:" + map1.containsKey("jack"));//判断集合中是否包含指定key
        System.out.println("containsvalues:" + map1.containsValue(100));
    }
}

5.1 HashMap

底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;

public class Demo {
    public static void main(String[] args){
        HashMap<Person,String> hashMap = new HashMap<Person,String>();
        hashMap.put(new Person("jack",20),"1001");
        hashMap.put(new Person("Aaron",20),"1002");
        System.out.println(hashMap);
        System.out.println(hashmap.put(new Person("rose",18),"10086"));

        Set<Entry<Person,String>> entrySet = new hashMap.entrySet();
        Iterator<Entry<Person,String>> it = entrySet.iterator();

        while(it.hasNext()) {
            Entry<Person,String> next = it.next();
            Person key = next.getKey();
            String value = next.getValue();
            System.out.println(key+"="+value);
        }
    }
}

class Person {
    private String name;
    private int age;

    Person() {}

    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    //set/get方法

    @Override
    public int hashCode(){
        return this.name.hashCode()+age*37;
    }

    @Override
    public String toString(){
        return "PersonName:"+this.name+"age:"+age;
    }
}

5.2 TreeMap

注意:Set的元素不可重复,Map的键不可重复,如果存入重复元素如何处理?

1.Set元素重复元素不能存入add方法返回false。
2.Map的重复健将覆盖旧键,将旧值返回。

6.使用Lambda表达式遍历集合

Java8 为Iterable接口新增方法forEach(),用来遍历集合中的元素。

注意:Iterator必须依附Collection对象。

本文标题:集合

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:09:39

原始链接:http://bangjinhu.github.io/undefined/Java 集合/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.