Java的集合

35 阅读3分钟

一、集合概述

1、数组和集合的元素存储个数问题

  • 数组定义后类型确定,长度确定。
  • 集合类型可以不固定,大小是可变的。

2、数组和集合存储元素的类型问题

  • 数组可以存储基本类型和引用类型的数据。
  • 集合只能存储引用数据类型的数据。

3、数组和集合适合的场景

  • 数组适合做数据个数和类型确定的场景。
  • 集合适合做数据个数不确定,且要做增删元素的场景。

二、Collection集合的体系特点

1、集合类体系结构

集合分为Collection单列和Map双列 。 Collection单列集合,每个元素(数据)只包含一个值。
Map双列集合,每个元素包含两个值(键值对)。

Collection集合体系:

image.png

Collection集合特点:

  • List系列集合:添加的元素是有序、可重复、有索引。
    • ArrayList、LinkedList:有序、可重复、有索引。
  • Set系列集合:添加的元素是无序、不重复、无索引。
    • HashSet:无序、不重复、无索引;LinkedHashSet:有序、不重复、无索引。
    • TreeSet:按照大小默认升序排序、不重复、无索引。 示例代码如下:
//有序 可重复 有索引
Collection list = new ArrayList();
list.add("Java");
list.add("Java");
list.add("Vue");
list.add(12);
list.add(12);
list.add(true);
list.add(true);
System.out.println(list);

//无序 不重复 无索引
Collection list2 = new HashSet();
list2.add("Java");
list2.add("Java");
list2.add("Vue");
list2.add(12);
list2.add(12);
list2.add(true);
list2.add(true);
System.out.println(list2);

其结果如下:

image.png

2、集合对于泛型的支持

集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型。

Collection<String> list = new ArrayList<>();
list.add("Java");
list.add("abc");
list.add(23);//会报错

注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象。

//集合和泛型不支持基本数据类型,只能支持引用数据类型
Collection<Integer> list2 = new ArrayList<>();
list2.add(22);

Collection<Double> list3 = new ArrayList<>();
list3.add(12.3);

三、Collecting集合常用API

Collection API 如下:
public boolean add(E e) 把给定的对象添加到当前集合中 。

// 1.添加元素, 添加成功返回true。
c.add("Java");
c.add("HTML");
System.out.println(c.add("HTML"));
c.add("MySQL");
c.add("Java");
System.out.println(c.add("数据库"));
System.out.println(c); // [Java, HTML, HTML, MySQL, Java, 数据库]

public void clear() 清空集合中所有的元素。

    // 2.清空集合的元素。
    c.clear();
    System.out.println(c);

public boolean remove(E e) 把给定的对象在当前集合中删除。

// 3.判断集合是否为空 是空返回true,反之。
System.out.println(c.isEmpty());

public boolean contains(Object obj) 判断当前集合中是否包含给定的对象。

// 4.获取集合的大小。
       System.out.println(c.size());

public boolean isEmpty() 判断当前集合是否为空。

// 5.判断集合中是否包含某个元素。
       System.out.println(c.contains("Java"));  // true
       System.out.println(c.contains("java")); // false
       System.out.println(c.contains("数据库")); // true

public int size() 返回集合中元素的个数。

// 6.删除某个元素:如果有多个重复元素默认删除前面的第一个!
       System.out.println(c.remove("java")); // false
       System.out.println(c);
       System.out.println(c.remove("Java")); // true
       System.out.println(c);

public Object[] toArray() 把集合中的元素,存储到数组中。

// 7.把集合转换成数组  [HTML, HTML, MySQL, Java, 数据库]
       Object[] arrs = c.toArray();
       System.out.println("数组:" + Arrays.toString(arrs));

       System.out.println("----------------------拓展----------------------");
       Collection<String> c1 = new ArrayList<>();
       c1.add("java1");
       c1.add("java2");
       Collection<String> c2 = new ArrayList<>();
       c2.add("a");
       c2.add("b");
       // addAll把c2集合的元素全部倒入到c1中去。
       c1.addAll(c2);
       System.out.println(c1);
       System.out.println(c2);

四、Collection集合的遍历方式

1、迭代器

迭代器遍历就是一个一个的把容器中的元素访问一遍,迭代器在Java中的代表是 Iterator,迭代器是集合的专用遍历方式。

Collection集合获取迭代器
Iterator<E> iterator()返回集合中的迭代器对象,该迭代器对象默认指向当前索引的0指引。

Iterator中的常用方法
boolean hasNext()询问当前位置是否有元素存在,存在返回true,不存在返回false。
E next()获取当前元素的位置,并同时将迭代器对象移向下一个位置,注意防止取出越界。

示例代码如下:

Collection<String> list = new ArrayList<>();
        list.add("aaaa");
        list.add("bbbb");
        list.add("cccc");
        list.add("dddd");
        System.out.println(list);//[aaaa, bbbb, cccc, dddd]

        //1、得到当前集合的迭代器对象
        Iterator<String> it = list.iterator();
//        String ele = it.next();
//        System.out.println(ele);//aaaa

        //2、定义while循环
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }

其最终结果为:

image.png

2、增强for循环

既可以遍历集合也可以遍历数组,其内部原理是一个Iterator迭代器,遍历集合相当于是迭代器的简化写法 ,实现Iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口。

格式:

for(元素数据类型 变量名 : 数组或者Collection集合){
    //在此处使用变量即可,该变量就是元素
}

举例:

Collection<String> list = new ArrayList<>();
···
for (String ele : list){
    System.out.println(ele);
}

3、Lambda表达式

示例代码:

Collection<String> list = new ArrayList<>();
···
list.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
});

//简化后
list.forEach(s ->{System.out.println(s);});

五、List系列集合

1、List系列集合特点

  • ArrayList、LinkedList:有序,可重复,有索引。
  • 有序:存储和取出的元素一致。
  • 有索引:可以通过索引操作元素。
  • 可重复:存储的元素可以重复。

2、List集合特有方法

List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。 public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。

public E get(int index):返回集合中指定位置的元素。

public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。

public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回更新前的元素值。

示例代码如下:

//1、创建一个ArrayList集合对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");

//2、在某个索引位置插入元素
list.add(2,"e");
System.out.println(list);//[a, b, e, c, d]

//3、根据索引删除元素,返回被删除元素
System.out.println(list.remove(2));//e
System.out.println(list);//[a, b, c, d]

//4、根据索引获取元素:public E set(int index, E element)
//返回修改前的数据
System.out.println(list.set(1,"f"));//b
System.out.println(list);//[a, f, c, d]

3、List集合的遍历方式

  • 迭代器
  • 增强for循环
  • Lambda表达式
  • for循环(因为List集合存在索引)

4、LinkedList集合特点

LinkedList的特点:
底层数据结构是双链表,查询慢,收尾操作的速度是极快的,所以多了很多首尾操作的特有的API

LinkedList集合的特有功能

public void addFirst(E e):将指定元素插入此列表的开头。

public void addLast(E e):将指定元素添加到此列表的结尾。

public E getFirst():返回此列表的第一个元素。

public E getLast():返回此列表的最后一个元素。

public E removeFirst():移除并返回此列表的第一个元素。

public E removeLast():移除并返回此列表的最后一个元素。

public E pop():从此列表所表示的堆栈处弹出一个元素。

public void push(E e):将元素推入此列表所表示的堆栈。

栈结构:

//LinkedList可以完成队列结构和栈结构(双链表)
LinkedList<String> stack = new LinkedList<>();
//压栈 入栈
stack.addFirst("a");
stack.addFirst("b");
stack.addFirst("c");
stack.addFirst("d");
System.out.println(stack);
//出栈 弹栈
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack);

当然也可以写为push和pop:

//压栈 入栈
stack.push("a");
stack.push("b");
stack.push("c");
stack.push("d");
System.out.println(stack);
//出栈 弹栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);

结果为:

image.png

队列结构:

//队列
LinkedList<String> queue = new LinkedList<>();
//入队
queue.addLast("1");
queue.addLast("2");
queue.addLast("3");
queue.addLast("4");
System.out.println(queue);
//出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);

结果为:

image.png

六、集合的并发修改异常问题

1、问题

当我们从集合中找出某个元素并并删除的时候可能出现一个并发修改异常问题。

2、存在的问题

  • 迭代器遍历集合且直接用集合删除元素的时候可能出现。
  • 增强for循环遍历集合且直接用集合删除元素的时候可能出现。

3、哪种遍历且删除不会出现问题

  • 迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
//删除全部的b信息
//迭代器的删除
Iterator<String> it = list.iterator();
while (it.hasNext()){
    String ele = it.next();
    if ("b".equals(ele)){
        //list.remove("b");//这么会报错
        it.remove();//删除当前所在元素,并且不会后移
    }
}
  • 使用for循环遍历并删除元素不会存在这个问题
for (int i = 0; i < list.size(); i++) {
    String ele = it.next();
    if ("b".equals(ele)){
        list.remove("b");
        i--;
    }
}