一文带你详读Java集合基础

为什么要使用集合

数组和集合都是Java中存储数据的容器

既然数组也可以存储数据,那为什么需要引入集合

来看一下数组的缺点

  1. 数组的大小一经确定不能改变,不适合动态存储

  2. 数组中提供的方法有限

而集合可以解决以上数组的缺点

Java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,有众多操作数据的方法

还需要注意的是:

数组可以存储基本数据类型,也可以存储引用类型

集合只能存储引用类型(基本数据类型会自动装箱为引用类型)

集合的分类

image.png
(图源:菜鸟教程)

Java 集合框架主要包括两种类型的容器,一种是Collection,存储一个元素集合,另一种是Map

  • Collection
    • List
      • ArrarList
      • LinkedList
      • Vector
    • Set
      • HashSet
        • LinkedHashSet
      • TreeSet
  • Map
    • HashMap
      • LinkedHashMap
    • TreeMap
    • ConcurrentHashMap
    • Hashtable

Collection常用方法

public boolean add(E e) //把给定的对象添加到当前集合中 。

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

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

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

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

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

public Object[] toArray()//把集合中的元素,存储到数组中
复制代码

List集合

List集合存储的数据是有序,可重复的

ArrayList

  • 优点: 底层数据结构是数组,查询快,增删慢。
  • 缺点: 线程不安全,效率高

来看一下ArrayList的源码分析(如何动态扩容):

  1. ArrayList的属性:

image.png

image.png

可以看出,默认的容量定义DEFAULT_CAPACITY为10

定义了两个Object[]空数组EMPTY_ELEMENTDATADEFAULTCAPACITY_EMPTY_ELEMENTDATA,当Arraylist为空时,将空数组赋值给ArrayList(两个数组不同的地方在于一个是默认返回的,一个是用户指定容量为0时返回)

elementData负责保存添加到ArrayList的元素

size表示ArrayList的实际大小

  1. ArrayList的构造方法:

image.png

有参的构造方法:初始化容量为0时,返回EMPTY_ELEMENTDATA

无参的构造方法:默认返回DEFAULTCAPACITY_EMPTY_ELEMENTDATA

  1. Add方法:动态扩容就是在add中实现的

image.png
add方法的执行步骤:

检查是否需要扩容,将元素插入elementData,数组长度也随之增加

image.png

Snipaste_2021-05-13_10-12-52.png

Vector

  • 优点: 底层数据结构是数组,查询快,增删慢。
  • 缺点: 线程安全,效率低

与ArrayList最大的区别就是Vector是同步的

当然如果ArrayList需要实现同步,可以使用Collections中的方法来实现

List list =Collcetions.synchronizedList(new ArrayList());
复制代码

LinkedList

  • 优点: 底层数据结构是链表,查询慢,增删快。
  • 缺点: 线程不安全,效率高

可以看到,LinkedList是通过双向链表来实现

image.png

Set集合

Set集合存储的数据是无序,不可重复的

无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数组的哈希值决定(元素虽然无放入顺序,但是元素在set中的位置是由该元素的HashCode决定的,其位置其实是固定的)

public void test1(){
        Set set=new HashSet();
        set.add(123);
        set.add("AA");
        set.add(new People("Jack",20));
        Iterator iterator=set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
复制代码

image.png

不可重复性:保证添加的元素按照equals()判断时,不能返回true

元素被加到数组中
先根据哈希值找到存储位置
新元素加入数组时,找到新元素的哈希值,通过算法计算出这个哈希值存储再底层数组中的位置,将新元素的哈希值与之前元素的哈希值对比,

哈希值不同,则两元素不同,元素添加成功。哈希值相同,再调用equals()方法比较两元素,返回结果为true则两元素相同

        set.add(123);
        set.add(123);
复制代码

image.png

HashSet

  • 线程不安全,效率高,允许存储null元素

HashSet底层:哈希表+红黑树

 HashSet 保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证。

LinkedHashSet

LinkedHashSet:线程不安全,效率高.

底层数据结构是哈希表+链表

在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据

TreeSet
可以按照添加元素的指定属性进行排序

底层数据结构是红黑树

向TreeSet中添加的数据,要求时相同类的对象

两种排序方式:自然排序(实现Comparable接口)
定制排序(Comparator)

遍历Collection

  1. 迭代器

Collection都继承了Iterator,所有Collection都可以使用iterator来遍历集合

iterator中的主要方法:

  • hasNext():判断是否还有下一个元素

  • next():指针下移,将下移以后集合位置的元素返回

        Iterator iterator =coll.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
复制代码
  1. 增强型for循环
        for(集合对象 : 集合)
复制代码

Map集合

Map中的存储的元素是两个对象,一个对象作为键,一个对象作为值。键不可以重复,但是值可以重复

Map中常见方法:

V put(K key, V value)  //将key,value放入map中

remove()  //删除指定key对象

clear()   //清空集合对象

boolean isEmpty()  //长度为0返回true否则false

boolean containsKey(Object key)  //判断集合中是否包含指定的key

value get(key) //判断键是否存在
复制代码

遍历Map的方式:

1、 将map集合中所有的键取出存入set集合。
Set<K> keySet()返回所有的key对象的Set集合,再通过get方法获取键对应的值。


2、 values() 
获取所有的值,但是这样不能获取到key对象


3、 Map.Entry对象
将map集合中的键值映射关系打包成Entry对象
Map.Entry对象通过Map.Entry 对象的getKey,getValue获取其键和值。
复制代码

方式一:

public void test1(){
        Map<String,Integer> map=new HashMap<String, Integer>();
        map.put("a",20);
        map.put("b",19);
        map.put("c",18);

        //方式一:将map集合转换为set集合,
        //keySet() 返回所有的key对象的Set集合
        //使用set中的迭代器遍历key
        //通过map中的get获取key的value
        Set<String> maptoset = map.keySet();
        Iterator iterator=maptoset.iterator();
        while(iterator.hasNext()){
            String key= (String) iterator.next();
            Integer value=map.get(key);
            System.out.println("key="+key+" " +"value="+value);
        }

    }
复制代码

方式二:

public void Test2(){
        Map<String,Integer> map=new HashMap<String, Integer>();
        map.put("a",20);
        map.put("b",19);
        map.put("c",18);

        //方式二:通过values获取所有值
        Collection<Integer> value = map.values();
        Iterator<Integer> integer=value.iterator();
        while(integer.hasNext()){
            Integer v = integer.next();
            System.out.println(v);
        }
    }
复制代码

方式三:

 public void Test3(){
        Map<String,Integer> map=new HashMap<String, Integer>();
        map.put("a",20);
        map.put("b",19);
        map.put("c",18);

        //方式三:entrySet
        //Map.Entry包含了key和value对象
        //需要返回的Map.Entry对象的Set集合
        Set<Map.Entry<String, Integer>> es= map.entrySet();
        Iterator<Map.Entry<String, Integer>> integer=es.iterator();
        while(integer.hasNext()){
            Map.Entry<String,Integer> me=integer.next();
            String key=me.getKey();
            Integer value=me.getValue();
            System.out.println("key="+key+" value"+value);
        }
    }
复制代码

HashMap:

底层是:数组+链表+红黑树

线程是不同步的,可以存入null键,null值

因为要保证键的唯一性,需要重写hashCode方法和equals方法

HashTable:

线程是同步的,不可以存入null键,null值

TreeMap:

底层是:红黑树

TreeMap可以对map集合中的键进行排序

但是需要使用Comparable或者Comparator 进行比较排序

方式一:元素自身具备比较性

需要让存储在键位置的对象实现Comparable接口,重写compareTo方法

方式二:容器具备比较性

定义一个类实现接口Comparator,重写compare方法

List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口

Collections工具类

是操作Collection和Map的工具类

常用方法:

sort(Collection)//对集合进行排序

binarySearch(Collection,Object)//查找指定集合中的元素,返回所查找元素的索引

reverse()//反转集合中元素的顺序

swap(List list,int i,int j)//交换集合中指定元素索引的位置
复制代码

举例:链表反转

        List coll =new ArrayList();
        coll.add(12);
        coll.add(10);
        coll.add(6);

        Collections.reverse(coll);
        System.out.println(coll);
复制代码

参考博客
Map参考博客

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享