当前位置: 首页 > news >正文

【从0开始学习Java | 第14篇】集合(上)

在这里插入图片描述

文章目录

  • 数组与集合的对比
  • 集合框架
  • Collection接口
    • 常用方法
    • Collection接口遍历元素的方式
      • 方式1:使用 iterator(迭代器)
      • 方式2 - for循环增强
      • 方式3 - 普通for循环
  • List接口
    • List接口常用方法
    • ArrayList 底层结构和源码分析
      • 注意事项
    • Vector 底层结构和源码剖析
      • Vector 与 ArrayList 的比较
    • LinkedList 底层结构和源码剖析
      • ArrayList 与 LinkedList 比较

数组与集合的对比

数组:在这里插入图片描述

我们使用数组实现增删改查操作:

import java.util.Arrays;public class ArrayUtil {private Object[] elements;private int size;public ArrayUtil() {this(16);}public ArrayUtil(int capacity) {elements = new Object[capacity];}public int size() {return size;}public void add(Object o) {if (size == elements.length) {//扩容当前容器容量的一半int length = elements.length + elements.length >> 1;elements = Arrays.copyOf(elements, length);}elements[size++] = o;}public void delete(Object o){if(o == null){return;}int index = -1;for(int i=0;i<size;i++){if(o.equals(elements[i])){index=i;break;}}System.arraycopy(elements,index+1,elements,index,size-index-1);size--;}public void update(int index,Object o){if(index<0||index>=size){throw new ArrayIndexOutOfBoundsException("下标越界了");}elements[index] = o;}public Object get(int index){if(index<0 || index>=size){throw new ArrayIndexOutOfBoundsException("下标越界了");}return elements[index];}
}

 使用数组对元素进行增删改查时,需要我们自己编码实现。而集合是Java平台提供的,也能进行增删改查,已经有了具体的实现。我们不需要再去实现,直接使用Java平台提供的集合即可,这无疑减少了编程的工作量。同时Java平台提供的集合无论是在数据结构还是算法设计上都具有更优的性能

集合:
在这里插入图片描述
这就是我们为什么要学习集合,他有诸多数组所不具备的优点

集合框架

在这里插入图片描述
 从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射

集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:

  • 接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象

  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。

  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序,这些算法实现了多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中

集合框架 如图所示:
在这里插入图片描述

Collection接口

常用方法

方法作用
int size()获取集合的大小
boolean isEmpty()判断集合是否存有元素
boolean contains(Object o)判断集合中是否包含给定的元素
Iterator iterator()获取集合的迭代器
Object [ ] toArray()将集合转换为数组
T [ ] toArray(T [ ] a)将集合转换为给定类型的数组并将该数组返回
boolean add(E e)向集合中添加元素
boolean remove(Object o)从集合中移除给定的元素
void clear()清除集合中的元素
boolean containsAll(Collection<?> c)判断集合中是否包含给定的集合中的所有元素
boolean addAll(Collection<? extends E> c)将给定的集合的所有元素添加到集合中

运用示例

import java.util.ArrayList;
import java.util.List;public class CollectionMethod {@SuppressWarnings("all")public static void main(String[] args) {List list = new ArrayList();list.add("java");list.add(10); // list.add(new Integer(10))list.add(true);System.out.println(list);list.remove(0);list.remove(true);System.out.println(list);System.out.println(list.contains(10));System.out.println(list.size());System.out.println(list.isEmpty());list.clear();System.out.println(list);ArrayList list2 = new ArrayList();list2.add("红楼梦");list2.add("三国演义");list.addAll(list2);System.out.println(list);System.out.println(list.addAll(list2));list.add("聊斋");list.removeAll(list2);System.out.println(list);}
}

运行结果:

[java, 10, true]
[10]
true
1
false
[]
[红楼梦, 三国演义]
true
[聊斋]

Collection接口遍历元素的方式

方式1:使用 iterator(迭代器)

基本介绍:

  1. Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
  2. 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
  3. Iterator仅用于遍历集合,它本身并不存放对象

注意:

在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常

举例:

package com;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;public class CollectionIterator {public static void main(String[] args){Collection col = new ArrayList();col.add(new Book("三国演义","罗贯中",10.1));col.add(new Book("红楼梦","曹雪芹",34.6));col.add(123);System.out.println(col);Iterator iterator = col.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}}class Book{private String name;private String author;private double price;public Book(String name, String author, double price) {this.name = name;this.author = author;this.price = price;}
}

运行结果:

[Book{name='三国演义', author='罗贯中', price=10.1}, Book{name='红楼梦', author='曹雪芹', price=34.6}, 123]
Book{name='三国演义', author='罗贯中', price=10.1}
Book{name='红楼梦', author='曹雪芹', price=34.6}
123

方式2 - for循环增强

增强for循环就是简化版的iterator,本质都是一样的

基本语法:
for(元素类型:集合名或数组名){
访问元素
}

例如:

for (Object o : col) {System.out.println(o);
}int[] a = {1, 2, 3, 4, 5};
for (int i : a) {System.out.print(i + " ");
}

方式3 - 普通for循环

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

List接口

List 接口是 Collection 接口的子接口

  • List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
  • List集合中的每个元素都有其对应的顺序索引,即支持索引
  • List容器中的元素都对应一个整数型的序号记载在其容器的位置,可以根据序号存取容器中的元素
  • 常用的有:ArrayLIstLinkedListVector

List接口常用方法

方法作用
void add(int index, Object ele)在index位置插入ele元素
boolean addAll(int index, Collection eles)从index位置开始将 eles中的所有元素添加进来
Object get(int index)获取指定index位置的元素
int indexOf(Object obj)返回obj在集合中首次出现的位置
int lastlndexOf(Object obj)返回obj在当前集合中末次出现的位置
Object remove(int index)移除指定index位置的元素,并返回此元素
Object set(int index, Object ele)设置指定index位置的元素为ele,相当于是替换.
List subList(int fromlndex, int tolndex)返回从fromlndex到 tolndex位置的子集合

方法演示:

package com;import java.util.ArrayList;
import java.util.List;public class List_ {public static void main(String[] args){List list = new ArrayList();list.add("张三丰");list.add("林七夜");// 在index = 1的位置插入一个对象list.add(1,"江梦南");System.out.println(list);List list2 = new ArrayList();list2.add("java");list2.add("cpp");list2.add("java");//从索引为1的位置加入list2的所有元素list.addAll(1,list2);System.out.println(list);System.out.println(list.indexOf("java"));System.out.println(list.lastIndexOf("java"));list.remove(0);System.out.println(list);list.set(1,"玛丽");System.out.println(list);// 返回的元素下标[0,2)的List relist  = list.subList(0,2);System.out.println("relist = "+relist);}
}

运行结果:

[张三丰, 江梦南, 林七夜]
[张三丰, java, cpp, java, 江梦南, 林七夜]
1
3
[java, cpp, java, 江梦南, 林七夜]
[java, 玛丽, java, 江梦南, 林七夜]
relist = [java, 玛丽]

ArrayList 底层结构和源码分析

  1. ArrayList中维护了一个Object类型的数 elementData
  2. 当创建对象时,如果使用的是无参构造器,则初始elementData容量为0(jdk7是10)
  3. 当添加元素时: 先判断是否需要扩容,如果需要扩容,则调用grow方法,否则直接添加元素到合适位置
  4. 如果使用的是无参构造器,如果第一次添加,需要扩容的话,则扩容elementData为10,如果需要再次扩容的话,则扩容elementData为1.5倍
  5. 如果使用的是指定容量capacity的构造器,则初始elementData容量为capacity
  6. 如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容elementData为1.5倍

注意事项

  • ArrayList 是由 数组 来实现数据存储的
  • ArrayList 基本等同 Vector,不过 ArrayList 是 线程不安全,所以在多线程情况下,不建议使用ArrayList

Vector 底层结构和源码剖析

  • Vector底层也是一个 对象数组:protected Object [ ] elementData
  • Vector 是线程同步的,即 线程安全

Vector 与 ArrayList 的比较

底层结构线程安全(同步)效率扩容倍数
ArrayList可变数组不安全,效率高如果有参构造1.5倍
如果是无参构造
1. 第一次10
2. 第二次开始按1.5倍扩容
Vector可变数组安全,效率不高如果是无参,默认10,满后,就按2倍扩容
如果指定大小,则每次按2倍扩容

LinkedList 底层结构和源码剖析

  1. LinkedList 底层维护了一个 双向链表
  2. LinkedList 中维护了两个属性 firstlast,分别只想 首节点 和 尾结点,其中通过 prev 指向前一个,通过 next 指向后一个节点,最终实现双向链表
  3. 所以LinkedList的元素的添加和删除,不是通过数组来完成的,相对来说效率较高

双向链表图示:
在这里插入图片描述
双向链表模拟示例:

package com;public class LinkedList_ {public static void main(String[] args) {Node jack = new Node("jack");Node Tom = new Node("Tom");Node Amy = new Node("Amy");jack.next = Tom;Tom.next =  Amy;Amy.prev = Tom;Tom.prev = jack;Node now = jack;while(now != null){System.out.println(now);now = now.next;}System.out.println("-----------");now = Amy;while(now != null){System.out.println(now);now = now.prev;}}
}class Node{public Object item;public Node prev;public Node next;public Node(String name){this.item = name;}public String toString() {return "Node{" +"name='" + item + '\'' +'}';}
}

运行结果:

Node{name='jack'}
Node{name='Tom'}
Node{name='Amy'}
-----------
Node{name='Amy'}
Node{name='Tom'}
Node{name='jack'}

ArrayList 与 LinkedList 比较

底层结构增删的效率改查的效率
ArrayList可变数组较低
数组扩容
较高
LinkedList双向链表较高,通过链表追加较低

如何选择 ArrayList 和 LinkedList:

  • 如果 改查 的操作较多,选择 ArrayList
  • 如果 增删 的操作多,选择 LinkedList
  • 一般来说,在程序中,80% ~ 90%都是查询,因此大部分情况下选择ArrayList

如果我的内容对你有帮助,请 点赞 评论 收藏 。创作不易,大家的支持就是我坚持下去的动力!
在这里插入图片描述

http://www.dtcms.com/a/337216.html

相关文章:

  • Day8--滑动窗口与双指针--1004. 最大连续1的个数 III,1658. 将 x 减到 0 的最小操作数,3641. 最长半重复子数组
  • 考问通系统测试分析报告
  • Golang 语言中的指针操作
  • Android中使用RxJava实现网络请求与缓存策略
  • 实习两个月总结
  • 通义万相Wan2.1- 阿里推出的开源视频生成大模型
  • 从哲学(业务)视角看待数据挖掘:从认知到实践的螺旋上升
  • Elasticsearch查询中的track_total_hits参数
  • 【网络安全实验报告】实验五:网络嗅探及安全性分析
  • 在阿里云 CentOS Stream 9 64位 UEFI 版上离线安装 Docker Compose
  • CentOS 7更换国内镜像源
  • CentOS 7安装OpenVASGVM指南
  • 国产!全志T113-i 双核Cortex-A7@1.2GHz 工业开发板—ARM + DSP、RISC-V核间通信开发案例
  • [数据结构] ArrayList 与 顺序表
  • OVS:ovn为什么默认选择Geneve作为二层隧道网络协议?
  • 【Day 30】Linux-Mysql数据库
  • 大数据计算引擎(三)——Elasticsearch入门
  • uart串口 day57
  • 产品经理如何提升职场学习能力?破除成长瓶颈
  • Vue+Flask 电影协同推荐可视化平台 前后端分离 大数据分析
  • Windows从零到一安装KingbaseES数据库及使用ksql工具连接全指南
  • 05.用户和组管理命令
  • 【机器学习】FPR(False Positive Rate,误报率)是什么?
  • Zephyr下ESP32S3开发环境搭建(Linux篇)
  • 深度研究系统、方法与应用的综述
  • Transformer架构的编码器和解码器介绍
  • 管理本地用户和组:红帽企业 Linux 系统安全的基础
  • TDengine `count_window` 指定列计数功能用户手册
  • 数据泵实施VPS海外:跨国数据同步的完整解决方案
  • elasticsearch-集成prometheus监控(k8s)