企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[toc] # 一、collection概述 collection是一个接口类,其有两个主要的子接口List和Set![](https://img.kancloud.cn/21/6b/216bd65ced1080975ec439e55d2b9d21_834x394.png) List(interface):List可以通过index知道元素的位置,它是<b>有序的</b>,并且允许元素<b>重复</b> Set(interface):<b>无序的,不允许元素重复</b> ## collection的方法 ![](https://img.kancloud.cn/26/8c/268cb09d82f4a78042d9bc9116934a1f_452x305.png) # 二、List ![](https://img.kancloud.cn/b8/81/b881759a0221c41dc1c3c7124399eed2_380x249.png) 有三个比较常用的继承List接口的类,分别是ArrayList、LinkedList和Vector List接口:有序的,此接口可以对列表中元素的插入位置精确控制,允许多个null值,元素可重复 ArrayList:ArrayList是个动态数组,擅长随机访问,非同步的 LinkedList:LinkedList是个双向链表,不能随机访问,非同步 Vector:Vctor是同步 的,其他同ArrayList类似,是线程安全的动态数组。 ## 2-1.ArrayList ArrayList就是一个<b>动态数组</b>,按照添加对象的顺序存储对象,<b>随机访问</b>速度极快。缺点是不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。 ~~~ //E是泛型,代表一种类型,让ArrayList里存的是同一种类型的数据 List<E> list = new ArrayList<>(); ~~~ ### ArrayList常用API set(int index,E element) ![](https://img.kancloud.cn/d5/96/d59665b49af781c687911668e5c09fed_624x57.png) remove(int index) ![](https://img.kancloud.cn/1b/e8/1be85a15261195ad5a2e4973860317e0_541x59.png) indexOf(Object o) ![](https://img.kancloud.cn/be/8d/be8d1f6eed8085e819e5e5a55b8439b3_864x60.png) ### ArrayList四个注意点 a.如果在初试化ArrayList的时候没有指定初始长度的话,默认长度为10 ~~~ /** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; ~~~ b.ArrayList在增加新元素的时候如果超过了原始的容量的话,ArrayList扩容ensureCapacity的方案为<b>“原始容量*1.5+1”</b> ~~~ public void ensureCapacity(int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } } ~~~ c.ArrayList是线程不安全的,在多线程的情况下不要使用。多线程用Vector。 d.ArrayList实现遍历的几种方法。 ~~~ public class Test{ public static void main(String[] args) { List<String> list=new ArrayList<String>(); list.add("Hello"); list.add("World"); list.add("HAHAHAHA"); //第一种遍历方法使用foreach遍历List for (String str : list) { //也可以改写for(int i=0;i<list.size();i++)这种形式 System.out.println(str); } //第二种遍历,把链表变为数组相关的内容进行遍历 String[] strArray=new String[list.size()]; list.toArray(strArray); for(int i=0;i<strArray.length;i++) //这里也可以改写为foreach(String str:strArray)这种形式 { System.out.println(strArray[i]); } //第三种遍历 使用迭代器进行相关遍历 Iterator<String> ite=list.iterator(); while(ite.hasNext()) { System.out.println(ite.next()); } } } ~~~ ## 2-2.LinkedList LinkedList是一个<b>双向链表</b>,优点在于适合于在链表中间需要频繁进行<b>插入和删除</b>操作,缺点是随机访问速度较慢,查找每个元素都需要从头开始一个个找。 遍历LinkedList ~~~ List<String> list=new LinkedList<String>(); list.add("Hello"); list.add("World"); list.add("HAHAHAHA"); for(String str:list){ System.out.println(str); } ~~~ ## 2-3.Vector vector和arraylist基本一致,区别在于vector中的绝大部分方法都使用了<b>synchronized</b>锁,使得vector多线程的情况下是安全的。vector是允许设置默认的增长长度,vector的默认扩容方式为原来的2倍。 # 三、Set set接口也是collection的一个常用子接口。set接口区别于list接口的特点在于: set中的元素不能重复,并且是无序的,最多允许一个null值。 注意:虽然set中元素没有顺序,但是元素在set中的位置是由该元素的hashcode决定的,其具体位置其实是<b>固定的</b>。 ## 1.HashSet ~~~ /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); } ~~~ 由hashset的部分源码可知,hashset的底层就是<b>基于hashmap</b>来实现的。 hashset使用和理解中容易出现的误区: a.hashset中存储元素的位置是固定的 hashset中存储元素是<b>无序的</b>,由于hashset底层是基于hash算法实现的,使用了hashcode,所以hashset中相应的元素的位置是固定的 b.遍历hashset的几种方法 ~~~ public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("hello"); set.add("world"); set.add("hello"); //1.使用foreach循环遍历 for(String str:set){ System.out.println(str); } //2.使用迭代器遍历 Iterator<String> iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } } ~~~ ## 2.LinkedHashSet ~~~ public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { private static final long serialVersionUID = -2851667679971038690L; /** * Constructs a new, empty linked hash set with the specified initial * capacity and load factor. */ ~~~ 由linkedhashset的源码可知,linkedhashset不仅是set的子接口,还是上面<b>hashset的子类</b>。 通过查看底层源码发现,其底层是基于linkedhashmap。 ### linkedhashset和hashset异同点 1.**hashset和linkedhashset都出于速度原因使用了散列,故都没排序,而linkedset使用了链表维护了元素的插入顺序** 2.hashset是linkedhashset的父类 ## 3.TreeSet treeset是一种<b>排序二叉树</b>。存入set集合中的值,会按照值的大小进行相关的排序操作,底层算法是<b>基于红黑树</b>来实现的。 ![](https://img.kancloud.cn/3d/16/3d16b70cd2d4e753ce88c6e01d992624_586x268.png) ### treeset和hashset的区别和联系 1.hashset是通过hashmap实现的,treeset是通过treemap实现的,只是用了map的key值。 2.map的key和set都有一个共同的特性就是集合的唯一性。treemap更是多了一个排序的功能。 3. hashCode和equal()是HashMap用的, 因为无需排序所以只需要关注定位和唯一性即可.            a. hashCode是用来计算hash值的,hash值是用来确定hash表索引的.            b. hash表中的一个索引处存放的是一张链表, 所以还要通过equal方法循环比较链上的每一个对象               才可以真正定位到键值对应的Entry.            c. put时,如果hash表中没定位到,就在链表前加一个Entry,如果定位到了,则更换Entry中的value,并返回旧value 4. 由于TreeMap需要排序,所以需要一个Comparator为键值进行大小比较.当然也是用Comparator定位的.            a. Comparator可以在创建TreeMap时指定            b. 如果创建时没有确定,那么就会使用key.compareTo()方法,这就要求key必须实现Comparable接口.            c. TreeMap是使用Tree数据结构实现的,所以使用compare接口就可以完成定位了.