数组、ArrayList、链表、LinkedList
数组
数组 | ||||
数组类型 |
不可重复 无序(线性查找) |
可重复(找到第一个即可) 无序(线性查找) |
不可重复 有序(二分查找) |
可重复(找到第一个即可) 有序(二分查找) |
插入 | O(N) |
O(1) |
O(logN+N) | O(logN+N) |
查询 | O(N) | O(N) | O(logN) | O(logN) |
删除(无洞) | O(N) | O(N) | O(lonN+N) | O(logN+N) |
总结 | 可重复无序插入快、下标已知更新查找快;查找删除慢、大小固定 | 查找快;插入删除慢、大小固定 | ||
应用 | 员工表,雇用解雇不经常发生 | |||
java数组 | 无序、可重复;插入快、查询删除慢、大小固定;如果已知下标,更新查找快 | |||
ArrayList | 大小可以扩展;但这是以牺牲效率为代价的 | |||
Vector | 大小可以扩展;但这也是以牺牲效率为代价的 |
java 数组(无序、可重复)
已知下标查找更新快O(1)
String str = strs[1];
strs[1] = "花";
查找慢O(N)
int index = findChar("花", strs);
删除慢O(N)
deleteChar("花", strs);
中部插入慢O(N)
insertCharWithMiddle("兴", 1, strs);
大小固定
public static void main(String[] args) {
String[] strs = {"中", "华", "人", "民", "共", "和", "国", null, null, null, null};
print(strs);
// 已知下标查找更新快
System.out.println(strs[1]);
strs[1] = "花";
print(strs);
// 查找慢,需要花费O(N)的时间
int index = findChar("花", strs);
if (index == strs.length) {
System.out.println("Can't find this char");
} else {
System.out.println("Find this char");
}
// 删除慢,需要花费O(N)的时间
deleteChar("花", strs);
print(strs);
// 中部插入慢,需要花费O(N)的时间
insertCharWithMiddle("兴", 1, strs);
print(strs);
} private static void insertCharWithMiddle(String str, int index, String[] strs) {
for (int i = strs.length - 2; i >= index; i--) {
strs[i + 1] = strs[i];
}
strs[index] = str;
} private static void deleteChar(String str, String[] strs) {
int index = findChar(str, strs);
if (index != strs.length) {
for (int i = index; i < strs.length - 2; i++) {
strs[i] = strs[i + 1];
}
strs[strs.length - 1] = null;
}
} public static int findChar(String str, String[] strs) {
for (int i = 0; i < strs.length; i++) {
if (strs[i].equals(str)) {
return i;
}
}
return strs.length;
} public static void print(String[] strs) {
System.out.println(Arrays.asList(strs));
}
ArrayList
末尾插入快,已知下标查找快更新快
一个参数的add("xxx")方法效率高O(1)
get(1)方法效率高O(1)——已知下标查找快
set(1, "xxx")方法效率高O(1)——已知下标更新快
中部插入、查询、删除慢
add(1, "xxx")方法效率低O(N)——中部插入
contains、indexOf方法效率低O(N)——查询慢
remove(1),remove("xxx")方法效率低O(N)——删除慢
数组固定大小,虽然ArrayList可以自动扩展,但是以牺牲效率为代价的。
public static void main(String[] args) {
List<String> list = new ArrayList<>(8);
// add方法效率高O(1)
list.add("中");
list.add("华");
list.add("人");
list.add("民");
list.add("共");
list.add("和");
list.add("国");
System.out.println(list);
// get(1),方法效率高O(1),如果知道下标查找快
System.out.println(list.get(1)); // add(1, "xxx")方法效率低O(N),中部插入慢
list.add(1, "花");
System.out.println(list);
// 删除慢O(N)
list.remove(1);
list.remove("人");
System.out.println(list);
// contains、indexOf方法都比较慢,需要O(N)的时间
System.out.println(list.contains("中"));;
System.out.println(list.indexOf("中"));
}
ArrayList容量扩展源码分析
package java.util; import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import sun.misc.SharedSecrets; public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable { // 默认容量是10
private static final int DEFAULT_CAPACITY = 10; // 默认最大容量是MAX_ARRAY_SIZE,实际可以扩展至Integer的最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; public boolean add(E e) {
// ensure /ɪn'ʃʊə/ 确保 Capacity /kəˈpæsəti/ 容量
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
// 原来数组的元素个数加上新集合的容量
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
// System.out.println(Math.max(10, 11)); 输出11,比较两个数字,返回大的数字
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 父类的一个成员变量,应该是修改次数的记录
modCount++;
// 数组容量不够
if (minCapacity - elementData.length > 0)
// grow /grəʊ/ 扩大
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// System.out.println(20 >> 1); 结果是20的二分之一,10
// 1、扩展后的数组是原来数组加上原来数组的一半,适用于add(E e)方法
// add(int index, E e)指定的下标越界会报异常,下标必须正确,不存在扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 2、扩展后的数组是指定的下标值,比如原有容量是10,addAll一个有8个元素的集合
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 3、扩展后的数组是Integer的最大值,默认的最大值是Integer.MAX_VALUE - 8
// private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
}
链表
除非频繁通过下标访问各个数据,否则都可以使用链表代替数组
链表也可以是有序的无序的,可重复的不可重复的
简单的一个链表定义
class Link {
private long id; // data
private String name; // data
private byte age; // data
private String address; // data
private Link next;
}
class LinkList {
private Link first;
}
最后一个元素的next引用是null
单链表
insertFirst();
deleteFirst();
isEmpty(); // 是否为空
find(); // 遍历,查找指定Link
delete(); // 遍历,删除指定Link
insertAfter(); // 遍历,插入
双端链表
新增对最后一个Link的引用
insertLast();
表头多次插入会颠倒Link插入的顺序;表尾多次插入会保持Link插入的顺序
双端链表也不能提高删除最后一个链接点的效率
链表的效率
表头插入查询删除快,O(1)
中部插入查询删除慢,需要O(N)次比较;在数组中执行这些操作也需要O(N)次比较,但是链表仍然要快一些,因为链表不需要移动元素,只需要比较,而复制时间大于比较时间
有序链表
只有insert()方法与无序链表中的insert()方法不同
效率:插入删除O(N),删除最小值O(1)
双向链表
每个Link多了一个指向前一个元素的引用
第一个元素指向前一个元素的引用是null
双向链表的缺点是每次插入或删除一个Link的时候,要处理四个链接点的引用,而不是两个
双向链表不必是双端链表
deleteLast();
链表迭代器
实现从链接点到链接点步进,以提高效率
java LinkedList
java里的LinkedList是一个双端链表、双向链表。
public static void main(String[] args) {
LinkedList<String> strings = new LinkedList<>();
strings.add("1");// 末尾添加
strings.add(1,"2");// 遍历,for循环的i和index比较,在Node里并没有成员变量index
strings.addFirst("3");
strings.addLast("4");
strings.contains("5");// 遍历
strings.element();//返回第1项
strings.get(1);// 遍历,for循环的i和index比较
strings.getFirst();
strings.getLast();
strings.indexOf("6");// 遍历,for循环里index递增并返回
strings.offer("7");// 末尾添加
strings.offerFirst("8");// 表头添加
strings.offerLast("9");// 末尾添加
strings.peek();// 返回第1项
strings.peekFirst();
strings.peekLast();
strings.poll();
strings.pollFirst();
strings.pollLast();
strings.pop();// 弹出第1项
strings.push("");// 添加到第1项
strings.remove(); //删除第1项
strings.remove(1);
strings.remove("10");
strings.removeFirst();
strings.removeLast();
}
java ArrayList和LinkedList
ArrayList底层是一个无序的可重复的数组,LinkedList底层是一个双端双向链表。
除非频繁地通过下标查询数据,否则都可以使用LinkedList来代替;LinkedList不需要扩容,直接在链表末尾添加元素,如果是添加一个集合,使用for循环。
首尾查询插入删除
ArrayList尾部插入快O(1)
LinkedList首尾插入快O(1)
中部查询插入删除
ArrayList中部插入删除O(N),N
LinkedList中部插入删除O(N),N/2
数组、ArrayList、链表、LinkedList的更多相关文章
- JAVA 基本数据结构--数组、链表、ArrayList、Linkedlist、hashmap、hashtab等
概要 线性表是一种线性结构,它是具有相同类型的n(n≥0)个数据元素组成的有限序列.本章先介绍线性表的几个基本组成部分:数组.单向链表.双向链表:随后给出双向链表的C.C++和Java三种语言的实现. ...
- 数组Array和列表集合ArrayList、LinkedList和Vector的区别
一.ArrayList和Vector的区别 ArrayList与Vector主要从以下方面来说. 1.同步性: Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同 ...
- C# 基础至集合-数组、List<T>、ArrayList、LinkedList、HashMap的一些区别
1:数组 ]; //赋值 strs[] = "; strs[] = "; //修改 strs[] = "burg"; //删除 没法删除 除非转化为可变数组li ...
- Collection集合重难点梳理,增强for注意事项和三种遍历的应用场景,栈和队列特点,数组和链表特点,ArrayList源码解析, LinkedList-源码解析
重难点梳理 使用到的新单词: 1.collection[kəˈlekʃn] 聚集 2.empty[ˈempti] 空的 3.clear[klɪə(r)] 清除 4.iterator 迭代器 学习目标: ...
- 深入理解java中的ArrayList和LinkedList
杂谈最基本数据结构--"线性表": 表结构是一种最基本的数据结构,最常见的实现是数组,几乎在每个程序每一种开发语言中都提供了数组这个顺序存储的线性表结构实现. 什么是线性表? 由0 ...
- ArrayList,Vector,LinkedList
在java.util包中定义的类集框架其核心的组成接口有如下:·Collection接口:负责保存单值的最大父接口 |-List子接口:允许保存重复元素,数据的保存顺序就是数据的增加顺序: |-Set ...
- Java数据结构之表的增删对比---ArrayList与LinkedList之一
一.Java_Collections表的实现 与c不同Java已经实现并封装了现成的表数据结构,顺序表以及链表. 1.ArrayList是基于数组的实现,因此具有的特点是:1.有索引值方便查找,对于g ...
- C++模拟实现JDK中的ArrayList和LinkedList
Java实现ArrayList和LinkedList的方式采用的是数组和链表.以下是用C++代码的模拟: 声明Collection接口: #ifndef COLLECTION_H_ #define C ...
- ArrayList与LinkedList用法与区别
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构. 2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedLis ...
- ArrayList 和 LinkedList 的区别
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构.2.对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动 ...
随机推荐
- Storm中的定时任务
1.全局定时器 import java.util.Map; import backtype.storm.Config; import backtype.storm.Constants; import ...
- Selenium自动化测试Python一:Selenium入门
Selenium入门 欢迎阅读Selenium入门讲义,本讲义将会重点介绍Selenium的入门知识以及Selenium的前置知识. 自动化测试的基础 在Selenium的课程以前,我们先回顾一下软件 ...
- Error:Could not determine the class-path for interface com.android.builder.model.AndroidProject.
Android Studio导入Eclipse项目报错Error:Could not determine the class-path for interface com.android.builde ...
- 【原创】基于Bootstrap的Modal二次封装
前言 Bootstrap:Twitter推出的一个开源的用于前端开发的工具包.它由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架 官方网站: ...
- python 生成唯一识别码
import uuid identity = str(uuid.uuid4()).encode('ascii')
- JDBC中链接数据库前为什么要用Class.forName(驱动类)加载驱动类?
使用JDBC链接数据库时,为什么要先使用Class.forName(String name)来加载类? 答: 实际上就是为了加载类时,调用静态初始化块中的注册函数. 可以看一下MySql的Driber ...
- java中 immutable,future,nio
什么是Future? 用过Java并发包的朋友或许对Future (interface) 已经比较熟悉了,其实Future 本身是一种被广泛运用的并发设计模式,可在很大程度上简化需要数据流同步的并发应 ...
- golang实现unicode码和中文之间的转换
将中文转换为unicode码,使用golang中的strconv包中的QuoteToASCII直接进行转换,将unicode码转换为中文就比较麻烦一点,先对unicode编码按\u进行分割,然后使用s ...
- 程序员必知的8大排序(三)-------冒泡排序,快速排序(java实现)
程序员必知的8大排序(一)-------直接插入排序,希尔排序(java实现) 程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现) 程序员必知的8大排序(三)-------冒 ...
- jmeter安装教程与新手入门(附jdk安装教程)
一.前言 最近要对网站做性能测试,提到了并发数测试,查了下,还是决定使用jmeter来完成这项测试,这里总结了jmeter完整的安装教程,附上新手使用教程. 二.jmeter安装 1.jdk安装(jm ...