Java中的Stack类
Stack(栈)是Vector的一个子类,实现了后进先出的栈,它的方法也是同步的,也是线程安全,跟父类一样的扩容方式
Stack的所有方法
empty —>测试堆栈是否为空
peek —>查看堆栈顶部的对象,不从堆栈中移除它
pop —>移除堆栈顶部的对象,并作为此函数的值返回该对象
push —>把项压入堆栈顶部
search —>返回对象在堆栈中的位置,以1为基数
Vector类
Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
AbstractList类提供了List接口的一个框架实现,以最小化由“随机访问”数据存储(例如数组)支持的接口实现所需的工作。对于顺序访问数据(例如链表),应该优先使用 AbstractSequentialList
List接口是一个有序集合(也称为序列)。这个接口的用户可以精确地控制列表中每个元素插入的位置。用户可以通过其整数索引(在列表中的位置)访问元素,并在列表中搜索元素。与集合不同,列表通常允许重复元素。
RandomAccess接口的主要目的是允许通用算法在应用于随机或顺序访问列表时改变它们的行为,从而提供良好的性能。
Cloneable接口表示该副本将包含对内部数据数组克隆的引用,而不是对这对象的原始内部数据数组的引用。
有四个构造方法
-
构造方法创建指定大小的向量,并且增量用capacityIncrement指定。增量表示向量每次增加的元素数目
public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); //初始化elementData,并给它初始化长度。默认就是10,除非自己给值。 this.elementData = new Object[initialCapacity]; //如果该值为0,每次增长按原来两倍 this.capacityIncrement = capacityIncrement; }
-
给空的vector构造器用和带有一个特定初始化容量,并且给容量增长值(capacityIncrement=0)
public Vector(int initialCapacity) { this(initialCapacity, 0); }
-
是一个空的构造方法,让他使用内置的数组(它调用了自身另外一个带一个参数的构造器)
public Vector() { this(10); }
-
按集合的迭代器返回元素的顺序排列。
public Vector(Collection<? extends E> c) { elementData = c.toArray(); elementCount = elementData.length; if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, elementCount, Object[].class); }
Vector的常用方法
public static void main(String[] args) {
// 初始化大小为3,每次增长2
Vector<Integer> v = new Vector<Integer>(3, 2);
System.out.println(v.capacity());
// 将指定的组件添加到此向量的末尾,将其大小增加 1
v.addElement(new Integer(1));
v.addElement(new Integer(2));
// 在此向量的指定位置2处插入指定的元素5
v.add(2, 3);
List<Integer> list = Stream.of(4, 5, 6).collect(Collectors.toList());
// 在指定位置将指定 Collection 中的所有元素插入到此向量中
v.addAll(3, list);
// 返回此容器的当前容量 ----->6
System.out.println(v.capacity());
// 查看此向量中是否包含此所有元素 --->true/false
System.out.println(v.containsAll(list));
Object[] lists = new Object[10];
// 将此向量的组件复制到指定的数组中
v.copyInto(lists);
System.out.println("返回向量中指定位置的元素:" + v.get(1));
// 返回此向量中第一次出现的指定元素的索引,从 index 处正向搜索,如果未找到该元素,则返回 -1。
System.out.println(v.indexOf(5, 2));
// 移除此向量中指定位置的元素
v.remove(0);
// 返回此向量中的组件数
v.size();
// 返回此部分视图,元素范围为从 fromIndex(包括)到 toIndex(不包括)
System.out.println(v.subList(0, 3));
}
Vector在你不需要线程安全的时候,也会给你加锁,导致了额外的开销,所以在jdk5之后就被弃用了
ArrayList和Vector的区别
ArrayList:线程不安全,在用iterator,会发生fail-fast,每次增加增加百分之50,有利于节约空间
Vector:线程安全,方法是同步的,因为在方法前面加了Synchronized关键字,所以也会发生fail-fast
fail-fast 机制(快速失败 )
是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。如用iterator迭代的时候,iterator就是另外起的一个线程,它去迭代collection,如果此时用collection.remove(obj)这个方法修改了collection里面的内容的时候,就会出现ConcurrentModificationException异常,这时候该迭代器就快速失败。
fail-safe
迭代是对原集合的Arrays.copyOf()(拷贝)进行遍历(产生大量的无效对象,开销大),所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触ConcurrentModificationException异常
在java.util下的集合都是发生fail-fast,而在java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改
Vector是线程安全,为什么是fail-fast呢
并不是说线程安全的集合就会报fail-safe,出现fail-safe是因为他们在实现增删的低层机制不一样
线程安全,为什么在迭代还会有别的线程对其进行修改等操作
我们在迭代的时候,根本就没有用到集合的修改操作,在迭代的过程中,有人拿了那把锁,我们也没有办法,因为那把锁就放在那边
ArrayList和LinkedList区别
ArrayList:底层是用数组实现的顺序表,是随机存取类型,可自动扩增
LinkedList:底层是用链表来实现的,是一个双向链表,注意这里不是双向循环链表,顺序存取类型
两个都是线程不安全的,在iterator时,会发生fail-fast