JDK提供了一些特殊的类,这些类可以存储任意类型的对象,并且长度可变,在Java中这些被称为集合。按照存储结构可以分为两大类,单列集合Collection和双列集合
常用集合类如下
Collection
Collection
为单列集合类的跟接口,用于存储一些列符合某种规则的元素。有三个重要的子接口,分别是List
和Set
以及Queue
。
List
特点是有序、可重复;Set
特点是元素无序且不可重复。List
接口的主要实现类有ArrayList
和LinkList
;Set
接口的主要实现类有HashSet
和TreeSet
方法声明 |
描述 |
boolean add(Object o) |
向集合添加一个元素 |
boolean addAll(Collection c) |
将指定的Collection中所有元素添加到该集合中 |
void clear |
删除该集合中的所有元素 |
boolean remove(Object o) |
删除该集合中的指定元素 |
boolean removeALL(Collection c) |
删除该集合中所有元素 |
boolean isEmpty() |
判断集合是否为空 |
boolean contains(Object o) |
判断集合是否包含某个元素 |
boolean containsAll(Collection c) |
判断该集合是否包含某个集合中的所有元素 |
Iterator iterator() |
返回在该集合的元素上进行迭代的迭代器,用于遍历该集合的所有元素 |
int size() |
获取该集合的元素个数 |
List
List
集合特点:线性存储,有序,可重复的;并且List集合的每个元素都有其对应的顺序索引,即支持索引,索引从0开始。
list不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法。
void add(int index, Object element) |
将元素element插入到List集合的index处 |
void addAll(int index, Collection c) |
将集合c所包含的所有元素插入到List集合的index处 |
Object get(int index) |
返回集合索引index处的元素 |
Object remove(int index) |
删除index索引处的元素 |
Object set(int index, Object element) |
将索引index处的元素替换为element对象,并将替换后的元素返回 |
int indexOf(Object o) |
返回对象o在List集合中出现的位置索引 |
int lastIndexOf(Object o) |
返回对象o在List集合中最后一次出现的位置索引 |
List subList(int fromIndex, int toIndex) |
返回从索引fromIndex(包括)到toIndex(不包括)处所有元素组成的子集合 |
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Demo01 { public static void main(String[] args) { List list = new ArrayList();//这里定义时没有指定存储类型如List<String> list = new ArrayList<>();默认可以存储任意类型,如果表明String,那么就只能存储String list.add("xml"); list.add(123); list.add('a'); System.out.println(list); Iterator iterator = list.iterator(); while (iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } for (Object obj : list){ System.out.println(obj); } System.out.println(list.toArray()); } }
ArrayList
ArrayList
是List
接口的一个实现类,也是程序中一种最常见的集合。
- ArrayList可以存放null值并且可以存放多个
- ArrayList底层使用数组实现的
- ArrayList扩容机制:
- ArrayList中维护了一个Object类型的数组elementData
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容为10;如果需要再次扩容,则扩容大小为elementData的1.5倍
- 如果使用指定大小的构造器,则初始容量为指定的大小,再次扩容则为elementData的1.5倍
- https://blog.csdn.net/weixin_42621338/article/details/82080167?spm=1001.2014.3001.5502
- ArrayList是线程不安全的,不过执行效率高。多线程下,不建议使用该集合。
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Demo01 { public static void main(String[] args) { List list = new ArrayList(); list.add("xml"); list.add(123); list.add('a'); list.add("xml"); System.out.println(list); Iterator iterator = list.iterator(); while (iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } for (Object obj : list){ System.out.println(obj); } System.out.println(list.toArray()); } }
LinkedList
LinkedList
是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度较慢。
另外,它还提供了List接口没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用(Java中使用的双向链表)
- LinkedList底层实现了双向链表和双端队列的特点
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last,分别指向首节点和尾节点
- 每个节点里面维护了prev、next、item三个属性;prev:指向前一个、通过next指向后一个节点,最终实现双向链表
- LinkedList中元素的添加或删除,不是通过数组实现的,效率相对较高
- 可以任意添加元素,元素可重复、可为null
- 线程不是安全的,没有实现同步
方法声明 |
描述 |
void add(int index, E element) |
指定位置插入指定元素 |
void addFirst(Object o) |
将指定元素插入列表开头 |
void addLast(Object o) |
将指定元素插入列表结尾 |
Object getFirst() |
返回列表的第一个元素 |
Object getLast() |
返回列表的最后一个元素 |
Object removeFirst() |
移除并且返回列表的第一个元素 |
Object removeLast() |
移除并且返回列表的最后一个元素 |
import java.util.*; public class Demo01 { public static void main(String[] args) { LinkedList link = new LinkedList(); link.add("xml"); link.add(123); link.add('y'); System.out.println(link); link.addFirst("xml"); link.addLast("ywj"); System.out.println(link); link.remove("xml");//如果有重复元素,只能移除第一个出现的 System.out.println(link); link.remove(3);//移除指定索引的元素 System.out.println(link); Object obj = link.removeFirst(); System.out.println(obj + " " + link);//移除开头的元素 } }
ArrayList和LinkedList比较
底层结构 |
增删效率 |
改查的效率 |
|
ArrayList |
可变数组 |
较低,通过数组扩容 |
较高,不需要改变数组 |
LinkedList |
双向链表 |
较高,通过链表追加 |
较低,需要通过node一个个遍历找到后操作 |
如何选择?
- 如果改查操作较多,选择ArrayList
- 如果增删操作较多,选择LinkedList
- 一般来说在程序中大多数都是查询,因此大部分情况下会选择ArrayList
- 不适合用于多线程,LinkedList和ArrayList的线程不是安全的
Vector(不推荐使用)
Vector
与ArrayList
一样也是通过数组实现的,不过不同的是它支持线程的同步,即在某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要的花费很高,因此访问它比访问ArrayList慢。
vector特点:
- 有序的,可以存储重复值和null值
- 底层是数组实现的,线程安全。结构和ArrayList非常相似,同样是一个线性的动态可扩容数组
- 初始容量是10,没有设置扩容增量的情况下以自身的2倍容量扩容;可以设置容量增量,初始容量和扩容两可以通过构造函数
public Vetor(int initialCapacity, int capacityIncrement)
进行初始化
查看Vector源码,和ArrayList几乎一模一样,区别就是Vector的方法使用了synchronized
锁实现了线程安全
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } public synchronized E remove(int index) { modCount++; if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); E oldValue = elementData(index); int numMoved = elementCount - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--elementCount] = null; // Let gc do its work return oldValue; }
那为什么很少见到使用Vector呢?
- 因为Vector虽然线程安全,但是每个可能出现线程安全的方法上加上了
synchronized
关键字,所以效率低。但是其实并没有很好的解决线程安全问题,比如下面这段代码,在判断是否包含某元素后会释放锁,在不包含的情况下,执行add之前,锁很有可能会被抢占
if (!vector.contains(element)) vector.add(element); }
- Vector空间满了以后扩容是一倍,而ArrayList仅是一半。如果数据太多可能会导致内存分配失败
- 只能在尾部进行插入和删除,效率较低
- 现在已经有了更好的替代品
CopyOnWriteArrayList
Set
Set
是无序且值不重复的Collection
。无序依赖于对象是否相等的判断,对象相等的本质是对象hashcode值判断的,如果想让两个不同的对象视为相等的,就必须覆盖Object的hashcode和equals方法。
HashSet
HashSet
的哈希表边存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List不相同),而是按照哈希值来存的所以取数据也是按照哈希值取。
元素的哈希值是通过元素的hashcode方法来获取,HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法,如果equals结果为true,HashSet就视为一个元素。如果equals为false就不是同一个元素。HashSet通过hashCode值来确定元素在内存中的位置。因此一个hashCode位置上可以存放多个元素。
import java.util.*; public class Demo01 { public static void main(String[] args) { Set set = new HashSet(); set.add("xml"); set.add('0'); set.add("xml"); set.add(1); set.add(null); set.add(2); Iterator iterator = set.iterator(); while (iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } } }
TreeSet
TreeSet
是使用二叉树的原理对新add()的对象按照指定的顺序排序(升序、降序),每增加一个对象都会进行排序,将对象插入到二叉树指定的位置。
Integer和String对象都可以进行默认的TreeSet排序,而自定义的对象是不可以的,自己定义的类必须实现Comparable
接口,并且覆盖写对应的compareTo()函数,才可以正常使用。在覆盖写compare()函数时,要返回相应的值才能使TreeSet按照一定的规则来排序,比较此对象和指定对象的顺序,如果该对象小于、等于或者大于指定对象,则返回负整数、零或正整数。
不过treeset存储的对象必须是同类型的对象,而且存储的类型必须实现了Comparable接口。如果没有实现该接口,那么add进入集合时进行元素大小比较的时候,调用comparableTo方法时会报错ClassCastException异常
import java.util.*; public class Demo01 { public static void main(String[] args) { Set set = new TreeSet(); set.add("xml"); set.add("ywj"); set.add("xml"); //set.add(null); 这里报错java.lang.NullPointerException,是不允许null值的 set.add("test"); Iterator iterator = set.iterator(); while (iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } } }
Iterator
Iterator主要用于迭代访问Collection中的元素,因此Iterator对象也被称为迭代器。案例如上面写的demo。
标签:元素,Java,一文,ArrayList,Object,list,Collection,add,集合 From: https://www.cnblogs.com/cccrush/p/16747296.html