数组、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要移动 ...
随机推荐
- 【从0到1学jQuery】jQuery中each()和$.each()的使用
引子: 最近遇到一个问题,就是在each()函数中怎么模拟for循环中的break和continue的操作.所以就查看了jQuery关于这个函数的文档,并且总结一下. 演示代码如下: <div& ...
- 【转】Windows 8 desktop app中dll搜索路径设置的诡异现象,Bug?
原文地址:http://blog.csdn.net/my_business/article/details/8850151 某个桌面程序在win 8上运行异常的问题困扰了我有近一周,今天终于找到了根本 ...
- Android_view的生命周期
onFinishInflate() 当View中所有的子控件均被映射成xml后触发 onMeasure( int , int ) 确定所有子元素的大小 onLayout( boolean , int ...
- Eclipse怎么样添加智能感知提示功能(含Windows版和Mac版)
近日感兴趣于安卓,开始学习Android开发……第一次使用Eclipse,用久了VS,也习惯了他的智能提示,刚转到Eclipse下实在是不习惯…… 网上有人说按Alt + / 可以实现单词补全功能,实 ...
- Hive执行过程中出现Caused by : java.lang.ClassNotFoundException: org.cloudera.htrace.Trace的错误解决办法(图文详解)
不多说,直接上干货! 问题详情 如下 这个错误的意思是缺少 htrace-core-2.04.jar. 解决办法: 将$HBASE_HOME/lib下的htrace-core-2.04.jar拷贝到$ ...
- postgresql 导出和导入数据库
使用 pg_dump 和 pg_restore 来备份和还原 postgresql的数据: 导出:pg_dump导入:pg_restore 最简单的导出命令如下:(导出指定数据库) $ pg_dump ...
- 「每日一码」a&b赋值问题
每日一码 将每天看到的优秀的代码或者特别的实现,记录下来 a&b赋值问题 2019-2-18 var a = {n: 1}; var b = a; a.x = a = {n: 2}; Q&am ...
- 自己写一个java的mvc框架吧(三)
自己写一个mvc框架吧(三) 根据Method获取参数并转换参数类型 上一篇我们将url与Method的映射创建完毕,并成功的将映射关系创建起来了.这一篇我们将根据Method的入参参数名称.参数类型 ...
- 221. 链表求和 II
假定用一个链表表示两个数,其中每个节点仅包含一个数字.假设这两个数的数字顺序排列,请设计一种方法将两个数相加,并将其结果表现为链表的形式. 样例 样例 1: 输入t:6->1->7 2-& ...
- Guava RateLimiter实现接口API限流
一.简介 Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率.RateLimit二的原理类似与令牌桶,它主要由许可发出的速率来定义,如果没有额外的配置,许可证将按每秒许可证规定的 ...