什么是java集合?
1、java集合大致分为Set、List、Queue、Map四种体系。
     List代表有序、可重复的集合;(有序指存储顺序和取出顺序一致)
     Set表示无序、不可重复(元素唯一)的集合;(无序指存储顺序和取出顺序不一致)
     Map代表具有映射关系的集合
     Queue代表一种队列集合
2、java集合就像一个容器,可以把多个对象(实际上是对象的引用,习惯都称之为对象)“丢进”该容器中。
3、java集合可以记住容器中对象的数据类型,从而编写出更简洁、简装的代码。


为什么要使用java集合?
1、在编程时,常常需要集中存放多个数据。可以使用数组来存放多个对象,但是数组的长度是不可变化的。在一开始定义了数组的长度之后,这个长度就是不可变化的,当数据量超过数组的长度之后,数组就无能为力了。
2、数组不能存储具有映射关系的数据。
3、数组只能存储同一类型的元素,而集合可以存储不同类型元素
4、数组可以存储基本数据类型,也可以存储引用数据类型;但是集合只能存储引用类型


为什么会出现集合类?
面向对象语言对事务的体现都是以对象的形式,所以为了方便对多个对象的操作,java就提供了集合类。


集合类的特点?
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。


集合只能存储引用类型,那么它要存储基本类型数据该怎么办呢?
在JDK5以后有了自动装箱的功能,JVM会把基本类型的数据自动转化为对应的包装类型,然后进行存储。
自动装箱和自动拆箱详解请看我这篇文章:http://blog.csdn.net/qq_36748278/article/details/77484436


如何访问不同集合中的元素?
1、如果访问List集合中的元素,可以直接根据元素的索引来访问
2、如果访问Map集合中的元素,可以根据每项元素的key来访问其value
3、如果访问Set集合中的元素,则只能根据元素本身来访问(因为把一个对象加入到Set集合时,Set无法记住添加这个元素的顺序,所以Set里的元素不能重复)


集合的使用步骤:
1、创建集合对象
2、创建元素对象
3、把元素添加到集合
4、遍历集合
    a、通过集合对象获取迭代器
    b、通过迭代器hasNext()方法判断是否有元素
    c、通过迭代器对象的next()方法获取元素并移动到下一个位置


Collection

Collection接口:集合的顶层接口
Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法即可用于操作Set集合,也可用于操作List和Queue集合。

Collection接口里定义了如下操作集合的方法:
1、boolean add(Object o):向集合里添加一个元素,成功添加则返回true。
2、boolean addAll(Collection c):把集合c里的所有元素添加到指定集合里,成功添加,返回true。

3、void clear():清除集合里的所有元素,将集合长度变为0。

5、boolean contains(Object o):判断集合中是否包含指定元素
6、boolean containsAll(Collection c):判断集合中是否包含集合c中的所有元素。

7、boolean isEmpty():判断集合是否为空。为空的时候返回true,否则返回false

8、boolean remove(Object o):删除集合中指定的o元素,当集合中含有多个o元素的时候,只删除第一个符合条件的元素,并将返回true
9、boolean removeAll(Collection c):从集合中删除集合c里包含的所有元素(相当于调用该方法的集合 - 集合c)

10、boolean retainAll(Collection c):从集合中删除集合c里不包含的元素(也就是把调用该方法的集合变成该集合和集合c的交集)(交集方法)
11、int size():返回集合里元素的个数
12、Object[] toArray():该方法把一个集合转换为数组,所有的集合元素变成对应的数组元素


什么是集合的继承体系结构?
由于需求不同,java就提供了不同的集合类。这些集合类的数据结构不同,但是他们都要提供存储和遍历功能的,把他们的共性不断向上提取,最终就像成了集合的继承体系图。

这里写图片描述


Iterator接口
Iterator iterator():返回一个Iterator对象,用于遍历集合中的元素


测试一下retainAll
假设有集合A和B,A调用此方法。把A与B的交集,交集的结果保存在A中,B中保持不变。
返回值表示A中内容是否发生过改变。

public class TestRtainAll {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        list.add("第一个元素");
        list.add("第二个元素");
        list.add("第三个元素");

        ArrayList<String> list1 = new ArrayList<String>();
        list1.add("第二个元素");
        list1.add("第四个元素");
        list1.add("第三个元素");

        boolean ret = list.retainAll(list1);
        System.out.println(ret);

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        System.out.println("------");

        for (int i = 0; i < list1.size(); i++) {
            System.out.println(list1.get(i));
        }
    }
}

输出结果为:

true
第二个元素
第三个元素
------
第二个元素
第四个元素
第三个元素

boolean ret = list.retainAll(list1);
调用retainAll方法之后,list集合就变成了list集合与list1集合的交集。而list1集合保持不变


测试一下aaddAll(Collection c)方法

public class CollectionDemo {
    public static void main(String[] args) {
        // 创建集合1
        Collection<String> c1 = new ArrayList<String>();
        c1.add("abc1");
        c1.add("abc2");
        c1.add("abc3");

        //创建集合2
        Collection<String> c2 = new ArrayList<String>();
        c2.add("def1");
        c2.add("abc2");     
        c2.add("def3");

        System.out.println(c1);

        c1.addAll(c2);
        System.out.println(c1);

        c1.removeAll(c2);
        System.out.println(c1);
    }
}

输出:

[abc1, abc2, abc3]
[abc1, abc2, abc3, def1, abc2, def3]    //可见ArrayList()里面的值可以重复
[abc1, abc3]

集合的三种遍历方式

集合的遍历—–Object[] toArray():把集合转换成数组,可以实现集合的遍历(不推荐)

public class CollectionDemo {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<String>();

        c.add("I");
        c.add("love");
        c.add("you");

        Object[] objs = c.toArray();        //数组里存放的是Object类型
        for(int i = 0;i < objs.length;i++){
            String s = (String)objs[i];     //把Object类型转换为String类型
            System.out.println(s);
        }
    }
}

输出:

I
love
you

集合的遍历—–增强for循环:

public class CollectionDemo {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<String>();

        c.add("I");
        c.add("love");
        c.add("you");

        for(int i = 0;i < c.size();i++){
            System.out.println(c.get(i));
        }
    }
}

输出:

I
love
you

集合的遍历—–Iterator迭代器。迭代器是依赖于集合而存在的。先有集合,再有迭代器。
Iterator是个接口。这个接口有3个方法:
boolean hasNext() :如果仍有元素可以迭代,则返回 true。
E next() : 返回迭代的下一个元素。 最初指向第一个元素的上面开始。
void remove() :从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。

public class CollectionDemo {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<String>();

        c.add("you");
        c.add("and");
        c.add("me");

        Iterator it = c.iterator();     //Iterator是个接口,所以实际返回的是子类对象。it是集合c的迭代器

        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

一定要记住next()的强大作用。一定要慎用next()方法,不要多次使用这个方法。因为每次使用都是访问一个对象
比如Iterator遍历一个集合中存放的是Student对象(有name和age属性)的情况的时候;

public class Test {
    public static void main(String[] args) {
        Collection<Student> c = new ArrayList<Student>();

        Student s1 = new Student("梨梨",21);
        Student s2 = new Student("熊熊",24);
        Student s3 = new Student("菜菜",10);

        c.add(s1);
        c.add(s2);
        c.add(s3);

        Iterator it = c.iterator();     //Iterator是个接口,所以实际返回的是子类对象。

        while(it.hasNext()){
            //System.out.println(((Student) it.next()).getName() + "-----" ((Student)it.next()).getAge());
            //报错,因为遍历最后一个对象的时候,next()在getName()的时候已经是最后一个对象元素了,在后面的getAge()的时候又进行了一次next()方法,所以越界了
            Student s = (Student)it.next();
            System.out.println(s.getName() + "----" + s.getAge());
        }
    }
}

迭代器为什么不定义成一个类?而是要定义成一个接口?
因为如果是实现类,它就必须提供具体实现。但是集合分为很多种不同的结构,它的每一个的实现类当然也就不同。


Collections类

Collections:针对集合操作的工具类,都是静态方法。

public static <T> void sort(List<T> list):排序。默认情况下是自然排序。
public static <T> int binarySearch(List<?> list,T key):二分查找
public static <T> T max(Collection<?> coll):最大值
public static void reverse(List<?> list):反转
public static void shuffle(List<?> list):随机置换

当ArrayList存储基本包装类时,Collections操作方法使用:

public class CollectionsDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<Integer> list = new ArrayList<Integer>();

        //添加元素
        list.add(30);
        list.add(50);
        list.add(10);
        list.add(40);
        list.add(20);

        System.out.println(list);

        //public static <T> void sort(List<T> list):排序,默认情况下是自然排序。
        Collections.sort(list);
        System.out.println(list);

        //public static <T> int binarySearch(List<?> list,T key):二分查找
        System.out.println(Collections.binarySearch(list, 30));
        System.out.println(Collections.binarySearch(list, 300));

        //public static <T> T max(Collection<?> coll):最大值
        System.out.println(Collections.max(list));

        //public static void reverse(List<?> list):反转
        Collections.reverse(list);
        System.out.println(list);

        //public static void shuffle(List<?> list):随机置换(没有固定的顺序)
        Collections.shuffle(list);
        System.out.println(list);
    }
}

输出:

[30, 50, 10, 40, 20]
[10, 20, 30, 40, 50]
2
-6
50
[50, 40, 30, 20, 10]
[10, 30, 20, 50, 40]

当ArrayList存储自定义对象的时候,Collections的这些静态方法的使用如下:
Student类:

public class Student2{
    private String name;
    private int age;

    public Student2() {

    }
    public Student2(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 class CollectionsDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        List<Student2> list = new ArrayList<Student2>();

        //创建学生对象
        Student2 stu1 = new Student2("caicai",21);
        Student2 stu2 = new Student2("lili",12);
        Student2 stu3 = new Student2("xiong",34);
        Student2 stu4 = new Student2("xiong",34);
        Student2 stu5 = new Student2("hehe",12);

        //添加元素对象
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        list.add(stu4);
        list.add(stu5);

        //排序
        Collections.sort(list);          //自然排序,报错

        //遍历集合
        for(Student2 s : list){
            System.out.println(s.getName() + "----" + s.getAge());
        }
    }
}

我们会发现,程序sort()方法报错了。为什么呢?我们来看看sort方法:

public static <T extends Comparable<? super T>> void sort(List<T> list)

可发现列表中的所有元素都必须实现 Comparable 接口。而此时列表中元素是Student类型,所以必须在Student类中实现Comparable 才行。
修改后的Student类如下:

public class Student2 implements Comparable<Student2>{
    private String name;
    private int age;

    public Student2() {

    }
    public Student2(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 int compareTo(Student2 s) {
        int num = this.age - s.age;
        int num2 = (num == 0 ? this.name.compareTo(s.name) : num);
        return num2;
    }
}

输出:

hehe----12
lili----12
caicai----21
xiong----34
xiong----34

上面问题还有一种解决办法:通过构造器排序

public static <T> void sort(List<T> list,Comparator<? super T> c)

我们可以重写这个Comparator来达到同样的目的。
如果同时又自然排序和比较器排序,以比较器排序为主

//法二:通过比较器
Collections.sort(list,new Comparator<Student2>() {
    @Override
    public int compare(Student2 s1, Student2 s2) {
        int num1 = s1.getAge() - s2.getAge();
        int num2 = (num1 == 0 ? s1.getName().compareTo(s2.getName()):num1);
        return num2;
    }
});

输出:

hehe----12
lili----12
caicai----21
xiong----34
xiong----34

Collection集合的比较:

List:有序,可重复。(存入和取出顺序一致)
        ArrayList:底层数据结构是数组, 查询快,增删慢。
                          线程不安全,效率高。
        Vector:底层数据结构是数组, 查询快,增删慢。
                       线程安全,效率低。
        LinkedList:底层数据结构是链表, 查询慢,增删快。
                           线程不安全,效率高。
Set:无序,唯一。
        HashSet:底层数据结构是哈希表。
                         保证元素唯一性:hashCode()和equals()方法。
        LinkedHashSet:底层数据结构是链表和哈希表
                         保证元素唯一性:哈希表
                         保证元素有序(存入和取出顺序一致,是特殊的Set):链表
        TreeSet:底层数据结构是红黑树。
                         保证元素排序:自然排序和比较器排序
                         自然排序(元素具有比较性):让元素所属的类实现Comparable接口
                         比较器排序(集合具有比较性):让集合接收一个Comparator的实现类对象
                         保证元素唯一性:根据比较的返回值是否是0来决定



List接口

List集合的特点是什么?
1、有序(存储和取出的元素一致)
2、可重复的。


List集合的三个子类各有什么特点?
ArrayList
1、底层数据结构是数组,查询快,增删慢。
2、线程不安全,效率高

Vector
1、底层数据结构是数组,查询快,增删慢。
2、线程安全,效率低

LinkedList
1、底层数据结构是链表,查询慢,增删快。
2、线程不安全,效率高。


List的3个子类都在什么情况下使用?
看自己的需求:
1、要求安全性:Vector(不过现在大部分都不用Vector)
2、不要求安全性:ArraayList或者LinkedList
     2.1、查询多:ArraayList
     2.1、增删多:LinkedList


ArrayList()方法里面为什么允许有重复的值存在?
我们看一下add()方法的源码:

public boolean add(E e) {
    ensureCapacity(size + 1);  
    elementData[size++] = e;
    return true;
}

我们可以看到他永远返回的都是true,也就是他每次添加都能成功,所以也就是可以添加重复的对象。


List集合特有的一些功能(父元素没有的功能)。 要注意索引的范围是否越界。
void add(int index, Object element) :在指定位置添加元素
Object get(int index):获取指定位置的元素
ListIterator listIterator():List集合特有的迭代器。
Object remove(int index):根据索引删除元素,返回被删除的元素
Object set(int index, Object element):根据索引修改元素,返回被修改的元素

public class ListDemo {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<String>();

        list.add("hello");
        list.add("you");
        list.add(1,"java");

        //list.add(4,"no");     //出错,索引越界了      
        list.add(3,"last");     //可以添加此位置

        System.out.println("get方法获取要获得的元素:" + list.get(1));
        System.out.println(list);

        System.out.println("set方法返回被修改的元素:" + list.set(2,"me"));
        System.out.println(list);
    }
}

输出:

get方法获取要获得的元素:java
[hello, java, you, last]
set方法返回被修改的元素:you
[hello, java, me, last]

List接口- - - - - ->List集合的特有的两种遍历方式

List集合特有的遍历:size()+get()方法结合(普通for循环)

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        list.add("today");
        list.add("is");
        list.add("yours");

        for(int i = 0;i < list.size();i++){
            String s = (String)list.get(i);
            System.out.println(s);
        }
    }
}

输出:

today
is
yours

List集合特有的迭代器— >列表迭代器ListIterator(它的父亲是Iterator)
该迭代器继承了Iterator迭代器,所以就可以直接使用hasNext()和next()方法。
特特有的功能是它也可以向前访问元素
Object previous():获取上一个元素。
boolean hasPrevious():判断是否有上一个元素。
此时就需要注意指针的位置。

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        list.add("today");
        list.add("is");
        list.add("yours");

        ListIterator<String> lit = list.listIterator(); 

        //此时指针在最前面,逆向遍历就没有元素。
        while(lit.hasPrevious()){
            String s = (String)lit.previous();
            System.out.println(s);
        }       
        System.out.println("------");

        while(lit.hasNext()){
            String s = (String)lit.next();
            System.out.println(s);
        }       
        System.out.println("------");

        //ListIterator还可以往前找元素。先正向后逆向即可
        while(lit.hasPrevious()){
            String s = (String)lit.previous();
            System.out.println(s);
        }
    }
}

输出:

------
today
is
yours
------
yours
is
today

List接口- - - - - ->Vector集合特有的功能(父类List集合没有的)

void addElement(Object obj):添加功能 ———————————– > 被add()替代
Object elementAt(int index):获取功能 ———————————— > 被get()替代
Enumeration elements():获取功能 ———————————– > 被Iterator iterator()替代
   boolean hasMoreElements()— —————————————— > 被hasNext()替代
   Object nextElement()—————————————————- > 被next()替代

只需要了解一下。不推荐使用了。

public class VectorDemo {
    public static void main(String[] args) {
        Vector<String> v = new Vector<String>();

        v.addElement("I");
        v.addElement("miss");
        v.addElement("you");

        //第一种方式
        for(int i = 0;i < v.size();i++){
            String s = (String)v.elementAt(i);
            System.out.println(s);
        }
        System.out.println("--------");


        //第二种方式
        //Enumeration是接口
        Enumeration<String> en = v.elements();  //返回的是实现类的对象
        while(en.hasMoreElements()){
            String s = (String)en.nextElement();
            System.out.println(s);
        }   
    }
}

输出:

I
miss
you
--------
I
miss
you

List接口- - - - - ->LinkedList集合特有的功能(父类List集合没有的)

addFirst(Object o):
addLast(Object o):
getFirst():
getLast():
removeFirst():
removeLast():

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> link = new LinkedList<String>();

        link.add("I");
        link.add("love");
        link.add("you");

        link.addFirst("first");
        link.addLast("last");

        System.out.println("removeFirst方法:" + link.removeFirst());
        System.out.println("removeLast方法:" + link.removeLast());

        System.out.println(link.getFirst());
        System.out.println(link.getLast());
        System.out.println(link);
    }
}

输出:

removeFirst方法:first
removeLast方法:last
I
you
[I, love, you]

并发修改异常

并发修改异常:ConcurrentModificationException
迭代器遍历集合,集合修改元素的时候会发生这个异常。
因为迭代器是依赖于集合而存在的,集合中新添加了元素,而迭代器却不知道,迭代器获取的还是修改之前的那个集合,所以会报错。这个错叫并发修改异常。
也就是说迭代器遍历元素的时候,集合是不可以修改元素的。

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        list.add("today");
        list.add("is");
        list.add("yours");

        //迭代器遍历。
        Iterator<String> it = list.iterator();

        while(it.hasNext()){
            String s = (String)it.next();
            if("is".equals(s)){
                //list.add("haha"); //报错。并发修改异常
            }
            System.out.println(s);
        }       
    }
}

解决方法:
    1、迭代器遍历元素,迭代器修改元素。Iterator没有添加方法,而它的子方法ListIterator有。
         元素是跟在他刚才迭代的元素后面的。
    2、集合遍历元素,集合修改元素。

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        list.add("today");
        list.add("is");
        list.add("yours");

        //迭代器遍历。迭代器添加
        ListIterator<String> lit = list.listIterator();
        while(lit.hasNext()){
            String s = (String)lit.next();
            if("is".equals(s)){
                lit.add("haha");
            }
        }   
        System.out.println(list);
    }
}

输出:[today, is, yours, haha]

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        list.add("today");
        list.add("is");
        list.add("yours");

        //集合遍历元素,集合修改元素
        for(int i = 0;i < list.size();i++){
            String s = (String)list.get(i);
            if("is".equals(s)){
                list.add("haha");
            }
        }
        System.out.println(list);
    }
}

输出:[today, is, yours, haha]



Set接口

Set集合:无序(存储顺序和取出顺序不一致),值不可以重复。但是虽然Set集合的元素无序,但是作为集合来说,他有自己的存储顺序。


Set接口- - - - - ->HashSet集合类

为什么HashSet存储字符串的时候,字符串相同的值只存储了一个呢?
可以去看我这篇文章:http://blog.csdn.net/qq_36748278/article/details/77842660


Set接口- - - - - ->LinkedHashSet集合类(继承HashSet集合类)

LinkedHashSet:特殊的set集合。底层数据结构由哈希表和链表组成。哈希表保证元素的唯一性,链表保证元素有序(存储和取出顺序是一致的)。


Set接口- - - - - ->TreeSet集合类

TreeSet:底层是二叉树。并且是红黑树。红黑树是一种自平衡二叉树。


TreeSet:能够对元素按照某种规则进行排序。
1、一种叫做自然排序。根据元素的自然顺序对元素进行排序
2、根据创建set时提供的Comparator进行排序。具体取决于使用的构造方法。

public TreeSet():构造一个新的空 set,该 set 根据其元素的自然顺序进行排序
public TreeSet(Comparator<? super E> comparator):构造一个新的空 TreeSet,它根据指定比较器进行排序。


TreeSet是如何保证元素的唯一性和排序的呢?
请看我这篇文章,有源码,解析的很透彻:http://blog.csdn.net/qq_36748278/article/details/77915801#t1


扩展

集合的toString方法的作用原理是什么呢?

public static void main(String[] args) {
    Collection c = new ArrayList();
    c.add("I");
    c.add("am");
    c.add("here");

    System.out.println(c);
}

输出:

[I, am, here]

现在我们就有个疑惑了,为什么打印输出c输出的不是地址而是值呢?
出现这种情况,我们应该就会猜想集合c应该是调用了toString()方法,所以才没有输出地址值。我们假设是调用了toString()方法。Collection c = new ArrayList();这是多态的用法,所以调用的也可定时ArrayList的toString方法才对。为了解决我们的疑惑,我觉得看源码是直接的方式。
于是我去ArrayList类中找toString方法,但是没有找到,那怎么办呢?只能去ArrayList类的父类中找啦,于是去它的父类AbstractList类中找,还是没有找到,于是我们就去AbstractList类的父类中找,终于找到了toString()方法。
这里写图片描述
代码如下:

public String toString() {
    Iterator<E> it = iterator();         //集合本身调用迭代器方法,得到集合迭代器
    if (! it.hasNext())                 //如果没有元素,就返回空
        return "[]";

    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = it.next();                     //如果有元素,就进行拼接
        sb.append(e == this ? "(this Collection)" : e);
        if (! it.hasNext())
            return sb.append(']').toString();
        sb.append(',').append(' ');
    }
}

杂记

1、ArrayList()方法默认的是构造一个初始容量为10的空列表。数据增长:当需要增长时,Vector 默认增长为原来一培,而ArrayList却是原来的一半 。
2、数组求长度用length属性;字符串String求长度用length()方法;集合求长度用size()方法
3、对象数组:数组即可以存储基本数据类型,也可以存储引用数据类型,它存储引用数据类型的时候的数组叫做对象数组。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐