全面了解Java集合

简介: 追根溯源全面上手集合(建议反复观看)

前言

设计集合的作者真的是太伟大了,就拿一个简简单单的add()方法,背后的动作在深入了解之后你会强烈感受到了人类的智慧

一.集合VS数组

数组完成定义并启动后,类型确定,长度固定。适合元素的个数和类型确定的业务情景,不适合进行CRUD的操作,就像一个铁盒子放的东西数量是固定的
在这里插入图片描述

集合的大小不固定,启动后可以动态改变,因此适合做数据个数不确定且需要增删元素的场景,集合就像一个气球,想变大就吹气!
在这里插入图片描述**

二.集合体系特点

**Collection单列集合:每个元素(数据)只包含一个值
==注==:set接口的实现子类也有存在两个值的(键值对[K-V]只不过其中的V是一个静态常量PRESENT)实际起作用的还是对象K(key)所以就把他也算作单列了
Map双列集合:每个元素包含两个值(键值对k-v)**
在这里插入图片描述

三.Collection单列集合

Collection这个接口有很多实现类,每一个类也包含了很多内部方法,主流的是List、Set这两个系列
以下是几个常用的实现类
在这里插入图片描述

性能对比(🏁)

**ArrayList:数据结构是数组,查询快,增删慢,线程不安全,效率高,存取顺序一致可重复
LinkedList:数据结构是链表,查询慢,增删快,线程不安全,效率高,存取顺序一致可重复
Vector(很少用):数据结构是数组,查询快,增删慢,线程安全,效率低,存取顺序一致可重复
HashSet:数据结构是哈希表,查询快,增删慢,线程不安全,效率高,存取顺序不一致不可重复
LinkedHashSet:数据结构是链表+哈希表,查询快,增删慢,线程不安全,效率高,存取顺序一致不可重复
TreeSet:数据结构是二叉树,查询快,增删慢,线程不安全,效率高,存取顺序不一致不可重复**

1.List系列常用API

List集合类中元素添加顺序和取出顺序一致、且可重复,List集合中的每个元素都有其对应的顺序索引
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
以ArrayList为例演示一下List接口实现类的常用方法

**1.add(E e):在集合末尾新增一个元素
2.add(int index, E element):在指定位置添加元素
3.get(int index):获取指定位置的元素
4.remove(int index) :删除指定位置的元素
5.remove(Object o): 删除指定元素
6.indexOf(Object o) :查询指定元素的位置 lastIndexOf也一样,只是从尾部开始遍历
7.set(int index, E element): 设置指定位置的元素值
8.retainAll(Collection<?> c) :求两个集合的交集
9.subList(int fromlndex, int tolndex) :获取【x,y)区间元素
10.set(int index, Object ele) :设置指定位置元素
11.indexOf(Object obj):返回元素在集合中首次出现的位置
12.lastIndexOf(Object obj):返回元素在当前集合中末次出现的位置**
........
  ArrayList col = new ArrayList();
  col.add("懒羊羊");  //添加单个元素
  col.add(100);  //col.add(new Integer(100))     
  col.remove("懒羊羊"); //删除指定元素
  col.remove(0); //删除第一个元素(索引)             
  System.out.println(col.size());  //获取元素个数
  System.out.println(col.isEmpty()); //看是否为空 
  col.clear();//清空
        
  ArrayList c1 = new ArrayList();
  c1.add("AA");
  col.addAll(c1); //把集合c1存进集合col
      
  ArrayList c2 = new ArrayList();
  c2.add("CC");
  c2.add("AA~");
  col.containsAll(c2); //查找col里是否有c2
  col.removeAll(c2);  //删除多个元素 删除col中的c2集合
  col returnlist=col.subList(0,2); //获取区间元素
  col.set(1, "喜羊羊");//设置指定位置元素
  col.indexOf("懒羊羊");//返回懒羊羊在集合中首次出现的位置
  col.lastIndexOf("懒羊羊");//返回懒羊羊在当前集合中末次出现的位置

==注==:每一个接口实现类的方法非常多,感兴趣可以自己去看看

2.Set系列常用API

和List系列集合一样,set系列集合也是Collection接口的实现类,所以用法和上述差不太多
==注==:set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null,set接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
内部方法大部分都一样,只不过不能使用索引来操作集合元素,因为set接口对象存放数据无序

   HashSet hs = new HashSet();
   hs.add(null);
   hs.add("懒羊羊");//添加单个元素
   hs.remove("懒羊羊"); //删除指定元素
   hs.remove(0); //删除第一个元素
   System.out.println(hs.size());  //获取元素个数
   System.out.println(hs.isEmpty()); //看是否为空 
   hs.clear();//清空

   HashSet c1 = new HashSet();
   c1.add("AA");
   hs.addAll(c1); //把集合c1存进集合hs

   HashSet c2 = new HashSet();
   c2.add("CC");
   c2.add("AA~");
   hs.containsAll(c2); //查找hs里是否有c2
   hs.removeAll(c2);  //删除多个元素 删除hs中的c2集合

四.Map双列集合

Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value,Map接口也有很多实现类,HashMap较为典型,下图是一部分实现类
在这里插入图片描述
HashMap:数据结构是哈希表,查询快,线程不安全,无序不重复
LinkedHashMap:数据结构是哈希表+链表,查询快,线程不安全,有序不重复

1.Map系列常用API

Map 中的 key和 value可以是==任何引用类型的数据==,会封装到HashMap$Node对象中,Map中的key不允许重复,value可以重复
Map的key可以为null, value也可以为null,注意key为null, 只能有一个,value为null可以有多个一般用String类作为Map的key
注:key和 value 之间存在==单向一对一==关系,即通过指定的key总能找到对应的value

**1.put(key,value):通过k-v添加元素
2.replace(3, "小灰灰"):在指定元素key的数据替换为新的value
3.clear():清空元素
4.isEmpty():判断是否为空返回boolean类型
5.put(new Object(),"灰太狼"):k-v中的K可以存放对象
6.get(key):用于获取指定K的元素
7.remove(key):删除指定元素**
Map map = new HashMap();
map.put( "1","懒羊羊");//通过k-v添加元素
map.put( "2","慢羊羊");//k-v
map.put("1","沸羊羊");//当有相同的k,就等价于替换
map.put( "3","美羊羊");//k-v
map.put(null, null); //可添加null
map.put(null,"喜羊羊");//通过相同的null等价替换
map.replace(3, "小灰灰");//在指定元素key的数据替换为新的value
map.get(3);//获取k为3的元素
map.size();//获取集合大小
map.clear();//清空元素
map.isEmpty(); //判断是否为空返回boolean类型
map.put(new Object(),"灰太狼");  //k-v

五.集合的遍历方式

集合的遍历就是一个一个的把容器中的元素访问一遍

1.迭代器遍历(🏁)

迭代器在Java中的代表是Iterator,有时又称光标(cursor)是程序设计的软件设计模式,迭代器是集合的专用遍历方式
1.首先要获得迭代器:

Collection col = new ArrayList();
Iterator m= col.iterator(); //获得集合对象col的迭代器 m

2.其次遍历集合:

while (m.hasNext()) {//使用while循环遍历 判断是否还有数据
     Object obj =m.next(); //动态绑定 取决于运行类型(什么样的对象)
     System.out.println(obj);//打印集合中的元素
}

完成迭代器遍历集合
==注==:如果要在当前基础上再次遍历还需要重新获取一下迭代器

2.增强for循环遍历

增强for循环遍历的本质还是利用迭代器

Collection col = new ArrayList();
for (Object o : col) {
            System.out.println(o);
        }

3.Lambda表达式遍历

从JDK8开始产生的新技术Lambda表达式遍历,是一种==更简单,更直接==的遍历集合的方式

Collection col = new ArrayList();
col.forEach(s ->{
   System.out.println(s);
});

4.Map遍历特例

1.遍历Map时,可以通过Key来遍历,首先要做的是取出所有的key,然后通过key得到value
具体操作:Collection keys = map.keySet();然后通过迭代器,增强for循环进行遍历,因为存在.get(key)方法,所以遍历过程中可以通过此方法得到集合中的所有元素
可以使用Collection的遍历方法本质也==是多态的体现==
在这里插入图片描述

2.第二种方式通过values遍历
具体操作:Collection values = map.values();同样也可以使用迭代器,增强for循环
注:遍历集合取出的是value
本质也是values()方法实现了Set接口,Set接口继承了Collection接口(==体现多态性==
在这里插入图片描述
3.第三种则是通过EntrySet获取k-v的方式遍历
具体操作:

Collection es = map.entrySet();
for (object entry : es) {
      Map.Entry m = (Map.Entry) entry;//将entry转型成成Map.Entry才能使用下面两个方法 
    system.out.println(m.getKey() + "" + m.getValue());
}

也可以选择迭代器遍历

六.集合存储对象过程

1.ArrayList集合底层原理(🏳️‍🌈)

ArrayList底层是基于数组实现的:
根据索引定位元素快,增删需要做元素的移位操作。第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
通过ArrayList的内部方法.add()来解读源码:

1.当创建ArrayList对象时,如果使用的是无参构造器,首先创建空的elementData数组 在这里插入图片描述
在这里插入图片描述
2.执行add()向空数组扩容 在这里插入图片描述
这个方法里包含了很多动作它先通过ensureCapacityInternal()判断是否要扩容
在这里插入图片描述
再通过calculateCapacity()判断elementData是否为空数组,若是则返回10,并将空数组扩容至为空间为10 在这里插入图片描述

3.当执行的add操作大于当前容量(10)则与当前空间大小比较并执行grow()方法进行扩容 在这里插入图片描述
4.前面我们知道初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍,注意没有拷贝数祖前里面的元素都是null
在这里插入图片描述

2.LinkedList集合底层原理(🏳️‍🌈)

原码解读:

1.首先也是通过构造器创建一个大小为0的链表,并且头尾都指向null 在这里插入图片描述
2.当执行add()添加操作时调用linkLast()方法 在这里插入图片描述
使用linkLast()将新的节点加入到双向链表尾部,也就是last指向“1”这个节点
在这里插入图片描述
同理也使用Linkedfirst()让first指向“1”这个节点

最后完成了add(1)的操作 将1添加到双向链表中
在这里插入图片描述

感想

我真是好后悔没把数据结构学到位
写到这里:==懒羊羊寿命-2年==

相关文章
|
2月前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
45 6
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
43 3
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
33 2
|
2月前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
34 3
|
26天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
27 2
|
1月前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
26天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
1月前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
1月前
|
Java 开发者
|
2月前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
60 5