集合

简介: 集合

一、集合框架体系



集合是Java中提供的一种容器,可以用来存储多个数据,根据不同存储方式形成的体系结构,就叫做集合框架体系。


image.png


image.png


每一种容器类底层拥有不同的底层算法。

既然数组可以存储多个数据,为什么要出现集合?

数组的长度是固定的,集合的长度是可变的。

使用Java类封装出一个个容器类,开发者只需要直接调用即可,不用再手动创建容器类。


集合中存储的数据,叫做元素,元素只能是对象(引用类型)。


二、 容器的分类



根据容器的存储特点的不同,可以分成三种情况:


image.png


image.png


  • List(列表):允许记录添加顺序,允许元素重复。
  • Set(集合):不记录添加顺序,不允许元素重复。
  • Map(映射):容器中每一个元素都包含一对key和value,key不允许重复,value可以重复。严格上说,并不是容器(集合),是两个容器中元素映射关系。
    注意:List和Set接口继承于Collection接口,Map接口不继承Collection接口




image.png
image.png


  • Collection接口:泛指广义上集合,主要表示List和Set两种存储方式。
  • List接口:表示列表,规定了允许记录添加顺序,允许元素重复的规范。
  • Set接口:表示狭义上集合,规定了不记录添加顺序,不允许元素重复的规范。
  • Map接口:表示映射关系,规定了两个集合映射关系的规范。
    注意:我们使用的容器接口或类都处于java.util包中。


三、List接口



List接口是Collection接口子接口,List接口定义了一种规范,要求该容器允许**记录元素的添加顺序,也允许元素重复。那么List接口的实现类都会遵循这一种规范。


List集合存储特点:


  • 允许元素重复
  • 允许记录元素的添加先后顺序
    该接口常用的实现类有:
  • ArrayList类:数组列表,表示数组结构,采用数组实现,开发中使用对多的实现类,
  • LinkedList类:链表,表示双向列表和双向队列结构,采用链表实现,使用不多。
  • Stack类:栈,表示栈结构,采用数组实现,使用不多。
  • Vector类:向量,其实就是古老的ArrayList,采用数组实现,使用不多。


1、List常用API方法


添加操作

boolean add(Object e):将元素添加到列表的末尾

  • void add(int index, Object element):在列表的指定位置插入指定的元素
  • boolean addAll(Collection c):把c列表中的所有元素添加到当前列表中
    删除操作
    Object remove(int index):从列表中删除指定索引位置的元素,并返回被删除的元素
  • boolean removeAll(Collection c):从此列表中移除c列表中的所有元素
    修改操作
    Object set(int index, Object ele):修改列表中指定索引位置的元素,返回被替换的旧元素
    查询操作
    int size():返回当前列表中元素个数
  • boolean isEmpty():判断当前列表中元素个数是否为0
    Object get(int index):查询列表中指定索引位置对应的元素
  • Object[] toArray():把列表对象转换为Object数组
  • boolean contains(Object o):判断列表是否存在指定对象
    注意,标红的是经常使用的方法。


2、ArrayList类


ArrayList类,基于数组算法的列表,通过查看源代码会发现底层其实就是一个Object数组。

操作List接口常用方法


public class ArrayListDemo1 {
    public static void main(String[] args) {
        //创建一个默认长度的列表对象
        List list = new ArrayList();
        //打印集合中元素的个数
        System.out.println("元素数量:"+list.size());//0
        //添加操作:向列表中添加4个元素
        list.add("Will");
        list.add(100);
        list.add(true);
        list.add("Lucy");
        //查询操作:
        System.out.println("列表中所有元素:"+list);//输出:[Will, 100, true, Lucy]
        System.out.println("元素数量:"+list.size());//4
        System.out.println("第一个元素:"+list.get(0));//Will
        //修改操作:把索引为2的元素,替换为wolfcode
        list.set(2, "wolfcode");
        System.out.println("修改后:"+list);//输出:[Will, 100, wolfcode, Lucy]
        //删除操作:删除索引为1的元素
        list.remove(1);
        System.out.println("删除后:"+list);//输出:[Will, wolfcode, Lucy]
    }
}


3、LinkedList类


  ArrayList类,基于数组算法的列表,通过查看源代码会发现底层其实就是一个Object数组。

      LinkedList类,底层采用链表算法,实现了链表,队列,栈的数据结构。无论是链表还是队列主要操作的都是头和尾的元素,因此在LinkedList类中除了List接口的方法,还有很多操作头尾的方法。

  • void addFirst(Object e) 将指定元素插入此列表的开头。
  • void addLast(Object e) 将指定元素添加到此列表的结尾。
  • Object getFirst() 返回此列表的第一个元素。
  • Object getLast() 返回此列表的最后一个元素。
  • Object removeFirst()  移除并返回此列表的第一个元素。
  • Object removeLast()  移除并返回此列表的最后一个元素。
  • boolean offerFirst(Object e) 在此列表的开头插入指定的元素。
  • boolean offerLast(Object e) 在此列表末尾插入指定的元素。
  • Object peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
  • Object peekLast() 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
  • Object pollFirst()  获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
  • Object pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
  • void push(Object e) 将元素推入此列表所表示的栈。
  • Object pop() 从此列表所表示的栈处弹出一个元素。
  • Object peek() 获取但不移除此列表的头(第一个元素)。


      LinkedList之所以有这么多方法,是因为自身实现了多种数据结构,而不同的数据结构的操作方法名称不同,在开发中LinkedList使用不是很多,知道存储特点就可以了。


public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList list = new LinkedList();
        //添加元素
        list.addFirst("A");
        list.addFirst("B");
        System.out.println(list);
        list.addFirst("C");
        System.out.println(list);
        list.addLast("D");
        System.out.println(list);
        //获取元素
        System.out.println("获取第一个元素:" + list.getFirst());//C
        System.out.println("获取最后一个元素:" + list.getLast());//D
        //删除元素
        list.removeFirst();
        System.out.println("删除第一个元素后:" + list);//[B, A, D]
        list.removeLast();
        System.out.println("删除最后一个元素后:" + list);//[B, A]
    }
}


4、Stack和Vector类


      Vector类:基于数组算法实现的列表,其实就是ArrayList类的前身。和ArrayList的区别在于方法使用synchronized修饰,所以相对于ArrayList来说,线程安全,但是效率就低了点。

      Stack类:表示栈,是Vector类的子类,具有后进先出(LIFO)的特点,拥有push(入栈),pop(出栈)方法。


四、集合元素迭代


1、集合元素遍历


对集合中的每一个元素获取出来。


List<String> list = new ArrayList<>();
list.add("西施");
list.add("王昭君");
list.add("貂蝉");
list.add("杨玉环");


使用for遍历


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


使用迭代器遍历

Iterator表示迭代器对象,迭代器中拥有一个指针,默认指向第一个元素之前,

  • boolean hasNext():判断指针后是否存在下一个元素
  • Object next():获取指针位置下一个元素,获取后指针向后移动一位


Iterator<String> it = list.iterator();
while(it.hasNext()) {
    String ele = it.next();
    System.out.println(ele);
}


操作原理如下图:


image.png

image.png


使用for-each遍历,推荐

语法:


for(元素类型 变量 : 数组/Iterable实例对象){
//TODO
}


这里Iterable实例对象表示,Iterable接口实现类对象,其实就是Collection,不包括Map。


for (String ele : list) {
    System.out.println(ele);
}


2、并发修改异常


需求:在迭代集合时删除集合元素,比如删除王昭君。


List<String> list = new ArrayList<>();
list.add("西施");
list.add("王昭君");
list.add("貂蝉");
list.add("杨玉环");
System.out.println(list);
for (String ele : list) {
    if("王昭君".equals(ele)) {
        list.remove(ele);
    }
}
System.out.println(list);


  此时报错java.util.ConcurrentModificationException,并发修改异常。


      造成该错误的原因是,不允许在迭代过程中改变集合的长度(不能删除和增加)。如果要在迭代过程中删除元素,就不能使用集合的remove方法,只能使用迭代器的remove方法,此时只能使用迭代器来操作,不能使用foreach。


List<String> list = new ArrayList<>();
list.add("西施");
list.add("王昭君");
list.add("貂蝉");
list.add("杨玉环");
System.out.println(list);
//获取迭代器对象
Iterator<String> it = list.iterator();
while(it.hasNext()) {
    String ele = it.next();
    if("王昭君".equals(ele)) {
        it.remove();
    }
}
System.out.println(list);


五、Set接口



      Set是Collection子接口,Set接口定义了一种规范,也就是该容器不记录元素的添加顺序,也不允许元素重复,那么Set接口的实现类都遵循这一种规范。


Set集合存储特点:

  • 不允许元素重复
  • 不会记录元素的添加先后顺序
    Set只包含从Collection继承的方法,不过Set无法记住添加的顺序,不允许包含重复的元素。当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false。
    Set接口定义了一种规范,也就是该容器不记录元素的添加顺序,也不允许元素重复。。
    Set接口常用的实现类有:
  • HashSet类:底层采用哈希表实现,开发中使用对多的实现类,重点。
  • TreeSet类:底层采用红黑树实现,可以对集合中元素排序,使用不多


1、HashSet类


HashSet底层采用哈希表实现,元素对象的hashCode值决定了在哈希表中的存储位置。

      当往HashSet集合中添加新的元素对象时,先回判断该对象和集合对象中的hashCode值:


  • 不等: 直接把该新的对象存储到hashCode指定的位置。
  • 相等: 再继续判断新对象和集合对象中的equals做比较。
  • 若equals为true:则视为是同一个对象,则不保存。
  • 若equals为false:存储在之前对象同槽位的链表上,此时操作比较麻烦,不讨论。
    在哈希表中元素对象的hashCode和equals方法的很重要。
    每一个存储到好像表中的对象,都得覆盖hashCode和equals方法用来判断是否是同一个对象,一般的,根据对象的字段数据比较来判断,通常情况下equals为true的时候hashCode也应该相等。
    Eclipse可以根据对象哪些字段做比较而自动生成hashCode和equals方法。
    需求1:操作Set接口常用方法


public class ArrayListDemo2 {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        //添加操作:向列表中添加4个元素
        set.add("Will");
        set.add("wolf");
        set.add("code");
        set.add("Lucy");
        //查询操作:
        System.out.println("集合中所有元素:" + set);//[code, wolf, Will, Lucy]
        System.out.println("元素数量:" + set.size());//4
        System.out.println("是否存在某个元素:" + set.contains("code"));//true
        System.out.println("是否存在某个元素:" + set.contains("code2"));//false
        //删除操作:删除code元素
        set.remove("code");
        System.out.println("删除后:" + set);//[wolf, Will, Lucy]
        //使用for-each遍历
        for (String ele : set) {
            System.out.println(ele);
        }
        //使用迭代器遍历
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            Object ele = it.next();
            System.out.println(ele);
        }
    }
}


六、Map接口


1、认识Map


2、Map常用方法


添加操作


boolean put(Object key,Object value):存储一个键值对到Map中


  • boolean putAll(Map  m):把m中的所有键值对添加到当前Map中
    删除操作
    Object remove(Object key):从Map中删除指定key的键值对,并返回被删除key对应的value
    修改操作
  • 无专门的方法,可以调用put方法,存储相同key,不同value的键值对,可以覆盖原来的。
    查询操作
  • int size():返回当前Map中键值对个数
  • boolean isEmpty():判断当前Map中键值对个数是否为0.
    Object get(Object key):返回Map中指定key对应的value值,如果不存在该key,返回null
  • boolean containsKey(Object key):判断Map中是否包含指定key
  • boolean containsValue(Object value):判断Map中是否包含指定value
    Set keySet():返回Map中所有key所组成的Set集合
    Collection values():返回Map中所有value所组成的Collection集合
  • Set entrySet():返回Map中所有键值对所组成的Set集合
    注意,标红的是重度使用的方法。


3、HashMap


HashMap底层基于哈希表算法,Map中存储的key对象的hashCode值决定了在哈希表中的存储位置,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复。


需求1:操作Map接口常用方法


public class HashMapDemo1{
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("girl1", "西施");
        map.put("girl2", "王昭君");
        map.put("girl3", "貂蝉");
        map.put("girl4", "杨玉环");
        System.out.println("map中有多少键值对:"+map.size());
        System.out.println(map);
        System.out.println("是否包含key为girl1:"+map.containsKey("girl1"));
        System.out.println("是否包含value为貂蝉:"+map.containsValue("貂蝉"));
        //替换key为girl3的value值
        map.put("girl3", "小乔");
        System.out.println(map);
        //删除key为girl3的键值对
        map.remove("girl3");
        System.out.println(map);
    }
}


Map的迭代遍历:


//获取Map中所有的key
Set<String> keys = map.keySet();
System.out.println("Map中所有key:"+keys);
//获取Map中所有的value
Collection<String> values = map.values();
System.out.println("Map中所有value:"+values);
//获取Map中所有的key-value(键值对)
Set<Entry<String, String>> entrys = map.entrySet();
for (Entry<String, String> entry : entrys) {
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key+"->"+value);
}



相关文章
|
4月前
|
存储 算法 C++
C++中集合的使用
C++中集合的使用
|
存储 Java 索引
1.9 集合
1.9 集合
38 1
|
存储 Java 容器
集合
集合
70 0
|
设计模式 安全
集合
集合
68 0
|
存储
16 集合(下)
16 集合(下)
101 0
|
存储 JavaScript 前端开发
集合的实现
集合的实现
集合的实现
|
存储 算法 安全
|
存储 Java 容器
|
存储 算法 安全
集合总结
集合总结
95 0