【集合】框架

发布时间:2026/8/13 21:09:31
【集合】框架 集合的由来java是面向对象语言它对事物的描述都是通过对象来体现的。为了方便对这些对象进行操作我们就必须把这些对象进行存储存储这些对象就不能是一个基本的变量而应该是一个容器类型的变量。数组和StringBuffer是容器类型的StringBuffer的结果是一个字符串不一定满足我们的要求所以只能选择数组这就是“对象数组”。而对象数组又不能适应变化的需求因为数组的长度是固定的为了适应变化的需求java就提供了集合供我们使用。数组和集合的区别1 长度区别① 数组的长度固定② 集合长度可变2 元素的数据类型不同① 数组可以存储基本数据类型也可以存储引用数据类型存储引用数据类型就是对象数组② 集合只能存储引用数据类型(集合的出现也就是用来存储对象的)集合是存储多个元素的存储多个元素也是有需求的比如多个元素中不能有相同的元素、多个元素按照某种规则排序一下。针对不同的需求集合也有很多类这些集合类的数据结构也不同。数据结构数据的存储方式。虽然数据结构不同但是它们有共性的内容(存储、获取、判断等)通过不断地向上提取就可以得到一个集合的继承体系结构图这个体系的老大是Collection。集合框架图Collection功能概述添加功能1 booleanadd(Object obj)2 booleanaddAll(Collection c)1 添加一个元素2 将集合c中的元素添加到另一个集合中另一个集合指的就是调用addAll()的集合删除功能1 void clear()2 booleanremove(Object o)3 boolean removeAll(Collection c)1 移除所有元素2 移除一个元素3 移除一个集合的元素。只要有一个元素被移除就返回true遍历功能IteratorEiterator()迭代器集合的专用遍历方式 。可以用while循环也可以用for循环遍历。iterator()返回的是Iterator是一个接口实际返回的是实现类的对象此接口方法有三个void remove()移除迭代器返回的最后一个元素boolean hasNext()判断是否有元素如果仍有元素就迭代返回trueObject next()获取元素并移动到下一个位置判断功能1 booleancontains(Object obj)2 boolean containsAll(Collection c)3 boolean isEmpty()4 boolean CollectionUtils.isEmpty(Collection)1 判断集合中是否包含指定元素注意contains()底层依赖的是equals()。案例如果是去除ArrayList集合中的重复自定义对象元素如果要被contains()包含的类对象中没有重写equals()则默认调用的是Object的equals()Object的equals()默认比较的是地址值所以地址值都是不同的所以被contains()包含的类对象中要重写equals()。2 判断集合中是否包含指定集合元素。只有包含所有的元素才返回true。3 判断集合是否为空。4 可以同时判断一个集合是否为空和是否为null是Spring框架提供的。如果为null或者为空就返回true长度功能intsize()返回元素的个数交集功能boolean retainAll(Collection c)两个集合都有的元素。返回的元素去哪了返回的boolean是什么意思两个集合存在交集调用该方法的集合存储的是交集的元素没有其他的元素返回true。两个集合不存在交集调用该方法的集合存储的是空值没有任何元素也返回true。假设有两个集合AB。A对B做交集最终的结果保存在A中B不变返回值表示的是A集合的元素是否发生过改变A集合原来保存的元素发生过改变则返回trueA集合原来保存的元素没有发生过任何改变则返回false。转换功能Object[] toArray()把集合转为数组。可以实现对数组的遍历SequencedCollection功能概述jdk21新增所有的有序集合都实现了这个接口由于是有序的集合它在Collection接口的基础上扩展了一些方法添加功能1 void addFirst(Object o)2 void addLast(Object o)1 向头部添加元素2 向末尾添加元素删除功能1 Object removeFirst()2 Object removeLast()1 删除头部元素2 删除末尾元素获取功能1 Object getFirst()2 Object getLast()1 获取头部元素2 获取尾部元素反转功能SequencedCollection reversed()反转集合中的元素返回新的集合原集合的元素并没有反转迭代器的使用及注意事项学生类省略... ...public class IteratorDemo { public static void main(String[] args) { Collection c new ArrayList(); Student s new Student(张三, 1); Student s2 new Student(李四, 2); Student s3 new Student(王五, 3); c.add(s); c.add(s2); c.add(s3); Iterator it c.iterator(); //通过集合获取迭代器对象。迭代器是依赖于集合而存在的 while (it.hasNext()) { //先判断 Student student (Student) it.next(); //在获取元素 System.out.println(student.getName(), student.getAge()); } } }上面除了可以使用while去循环遍历之外也可以使用for循环。it是局部变量for循环执行完毕就是垃圾提高内存利用率。只编写for循环部分如下for (Iterator it c.iterator(); it.hasNext(); ) { //没有条件控制 Student student (Student) it.next(); System.out.println(student.getName(), student.getAge()); }注意事项/* * 如果将以下的注释用链式编程去编写如果集合的元素是奇数个则会报错java.util.NoSuchElementException */ Iterator it c.iterator(); while (it.hasNext()) { //Student student (Student) it.next(); //System.out.println(student.getName() , student.getAge()); System.out.println(((Student) it.next()).getName() , ((Student) it.next()).getAge()); }从控制台的输出可以看出张三的年龄为2实际是1。之所以出现这样的问题是因为Iterator接口的next()是获取元素并移动到下一个位置。这里第二次调用next()就移动到了李四的年龄了再移动就是王五的姓名最后没有另一个人年龄了就报错。如果是偶数个不会报错但是遍历出来的数据是错乱的。所以不能多次使用next()因为每次访问都是一个对象迭代器的原理及源码解析迭代器为什么不定义成一个类而是一个接口假设迭代器定义成一个类这样就可以通过创建该类的对象调用该类的方法来实现集合的遍历定义成迭代器类就是一个具体实现具体实现这些集合类存储元素的方式就是一样的。BUT java中提供了这么多的集合类而这些集合类的数据结构都是不同的所以存储元素的方式应该是不同的进而遍历的方式也应该是不一样的所以就没有定义成一个迭代器类。而无论是哪种集合遍历集合都应该具备获取元素以及判断的功能在获取元素之前先做判断这样就不容易出错。而每种集合的方式又不太一样所以这两个常用的判断跟获取元素功能给提取了出来并不提供具体实现这种方式就是接口。那么具体的实现类在哪呢答案是在具体的实现类中以成员内部类的方式来体现的。源码如下//Iterator接口定义了如下两个方法 public interface Iterator { boolean hasNext(); Object next(); } public interface Iterable { Iterator iterator(); //该方法返回的是Iterator接口 } public interface Collection extends Iterable { Iterator iterator(); } public interface List extends Collection { Iterator iterator(); //到这还没有具体实现因为List还是接口 } public class ArrayList implements List { public Iterator iterator() { //具体子类实现了 return new Itr(); } private class Itr implements Iterator { //Itr重写了这两个方法 public boolean hasNext() {} public Object next(){} } }//遍历集合元素 Collection c new ArrayList(); c.add(hello); c.add(world); c.add(java); Iterator it c.iterator(); //new Itr(); while(it.hasNext()) { String s (String)it.next(); System.out.println(s); }以上遍历集合元素代码解析通过c集合对象获取迭代器对象在多态中通过父类对象访问成员方法的特点中如果子类重写了父类的方法输出的结果是子类成员方法重写父类成员方法后的内容。编译看左边Collection有iterator()不报错通过运行看右边子类ArrayList有iterator()所以输出的是子类的iterator()iterator()返回的是new Itr()Itr是一个具体的实现类所以如果返回的是一个接口实际返回的是实现类的对象。List集合特点有序(存储顺序和取出顺序一致)、可重复List集合特有功能概述添加功能1 void add(int index, Object element)2 boolean addAll(int index, Collection? extends E c)1 在指定索引位置插入元素2 在指定索引位置插入一个集合中的所有元素删除功能Object remove(int index)根据索引删除元素返回被删除的元素修改功能Object set(int index, Object element)根据索引修改元素返回被修改的元素获取(遍历)功能Objectget(int index)获取指定位置的元素遍历功能ListIterator listIterator()列表迭代器。List集合特有的迭代器listIterator()返回ListIterator是一个接口继承了Iterator。常用的方法有boolean hasPrevious()与Iterator接口的hasNext()一样只不过是逆向判断是否有元素。Object previous()与Iterator接口的next()一样只不过是获取上一个元素。注意要实现逆向遍历首先必须实现正向遍历所以一般无意义不使用其它功能1 int indexOf(Object o)2 int lastIndexOf(Object o)3 ListE subList(int fromIndex, int toIndex)1 从前往后查找指定元素的索引不存在返回-12 从前往后查找指定元素最后一次出现的索引值不存在返回-13 包含开始不包含结束在此区间截取一个新的List集合原始集合不变化List 集合特有遍历功能(普通for)size()结合get()public class ListDemo { public static void main(String[] args) { List l new ArrayList(); l.add(王麻子); l.add(张飞); l.add(武松); for (int i 0; i l.size(); i) { String s (String) l.get(i); System.out.println(s); } } }并发修改异常的产生原因及解决办法public static void main(String[] args) { List l new ArrayList(); l.add(王麻子); l.add(张飞); l.add(武松); Iterator it l.iterator(); while (it.hasNext()) { String s (String) it.next(); if (张飞.equals(s)) { l.add(好汉); } } System.out.println(l); }产生原因以上的代码逻辑看似没有问题如果两个字符串内容相等则添加新的元素。但是却报错了之所以报错是因为迭代器是依赖集合而存在的当判断到两个字符串内容相等就给集合添加了一个新的元素之后在用迭代器的hasNext()判断迭代器却不知道集合里面有新的元素所以就报错了这个错叫并发修改异常也就是说迭代器遍历元素的时候通过集合是不能修改元素的。解决办法集合的创建、往集合添加元素省略方法1 迭代器遍历元素迭代器修改元素/* * 方法1 * 因为Iterator迭代器没有添加功能它的子接口ListIterator有 */ ListIterator it l.listIterator(); while (it.hasNext()) { String s (String) it.next(); if (张飞.equals(s)) { it.add(好汉); } } System.out.println(l);方法2 集合遍历元素集合修改元素(普通for)//方法2 for (int i 0; i l.size(); i) { String s (String) l.get(i); if (张飞.equals(s)) { l.add(梁山); } } System.out.println(l);方法1与方法2的区别是方法1添加的新元素位于要比较元素的后面方法2新添加的元素在整个元素的最后面ArrayList集合特有功能概述ArrayList集合底层数据结构是数组随机增删慢末尾元素增删快修改快因为是先查询到指定元素的索引再修改所以修改是快的查询快线程不安全效率高转换功能public T T[] toArray(T[] a)把集合转成数组Vector集合特有功能概述Vector集合类底层数据结构是数组所以查询快增删慢线程安全所以效率低添加功能void addElement(Object obj)添加功能jdk1.0出现的被add()替代获取功能1 Object elementAt(int index)2 Enumeration elements()1根据索引获取类似于get() 。jdk1.0出现。被get()替代2获取多个元素类似于迭代器。jdk1.0出现。被Iterator iterator()替代elements()返回Enumeration是一个接口实际返回的是实现类的对象该接口有2个方法boolean hasMoreElements()被hasNext()替代Object nextElement()被next()替代LinkedList集合特有功能概述LinkedList底层数据结构是链表所以查询慢随机增删快末尾元素增删更快线程不安全所以效率高添加功能1 void addFirst(Object obj)2 void addLast(Object obj)1 将指定元素插入到元素的开头位置处。新添加的元素总是在最前面 。2 将指定元素插入到元素的末尾位置处获取功能1 Object getFirst()2 Object getLast()1 获取集合中的第一个元素。集合元素不变化2 获取集合中的最后一个元素。集合元素不变化删除功能1 Object removeFirst()2 Object removeLast()1 删除集合中的第一个元素并返回(获取)删除的元素2 删除集合中的最后一个元素并返回删除的元素Arrays工具类功能概述转换功能public static T ListT asList(T... a)将数组转换为集合已这种方式创建集合给定初始容量。需要注意的是1、只能针对对象数组进行转换对基本数据类型的数组转换不了2、转换后的集合不能够对其进行增删元素但可以修改元素转换后的集合是一个只读集合但又完全不是只读集合因为可以修改集合的元素适用场景希望别人不可以增删元素的时候使用排序功能public static void sort(Object[] a)对指定对象数组进行排序对asList()的使用代码如下// 例 public class ArraysDemo { public static void main(String[] args) { String[] arr {hello, world, java}; ListString list Arrays.asList(arr); //list.add(javaEE); //是错误的虽然转成了集合但本质还是数组不能改变集合的大小也就是不能增删元素但可以修改元素会报错UnsupportedOperationException for (String s : list) { //遍历集合。除了可以使用迭代器还可以使用foreach System.out.println(s); } } }//因为方法是可变参数所以可以省略定义数组的步骤直接这样编写 ListString list Arrays.asList(hello, world, java);注意事项此时虽然将一个数组转换为了一个集合但是不能改变集合的大小其本质还是一个数组。因为参数是以数组的形式保存下来的数组的长度不可变。如果给集合添加、删除元素则报错UnsupportedOperationExceptionSet集合特点无序(存储顺序和取出顺序不一致)、唯一HashSet集合类底层数据结构是哈希表线程不安全所以效率高。哈希表结构底层依赖HashCode()和equals()。哈希表是一个元素为链表的数组综合了数组和链表的优点。HashSet保证元素唯一的源码解析为什么List集合是可重复的而Set集合是唯一的呢保证Set集合唯一的是它的add()源码如下interface Collection{ ... } interface set extends Collection{ ... } class HashSet implements Set{ private static final Object PRESENT new Object(); private transient HashMapE, Object map; public HashSet{ map new HashMap(); //可以看出HashSet底层是HashMap } public boolean add(E e){ return map.put(e, PRESENT) null; //调用了HashMap的put() } } class HashMap implements Map{ public V put(K key, V value) { //看哈希表是否为空如果为空就开辟空间 if (table EMPTY_TABLE) { inflateTable(threshold); } //判断对象是否为null if (key null) return putForNullKey(value); int hash hash(key); //算哈希值调用了hash()。和添加进来元素对象的hashCode()相关 //在哈希表中查找hash值 int i indexFor(hash, table.length); for (EntryK,V e table[i]; e ! null; e e.next) { Object k; if (e.hash hash ((k e.key) key || key.equals(k))) { V oldValue e.value; e.value value; e.recordAccess(this); return oldValue; //走这里其实是没有添加元素 } } modCount; addEntry(hash, key, value, i); //把元素添加 return null; } transient int hashSeed 0; //hash() final int hash(Object k) { int h hashSeed; if (0 ! h k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h ^ k.hashCode(); //这里调用的是添加进来元素对象的hashCode() // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^ (h 20) ^ (h 12); return h ^ (h 7) ^ (h 4); } }通过查看add()源码可以看出这个方法底层依赖两个方法hashCode()和equals()。也就是说由这两个方法保证元素唯一性的。步骤首先比较哈希值如果相同继续走比较地址值或者比较equals()如果不同就直接添加到集合中按照方法的步骤来说先看hashCode()值是否相同相同继续比较equals()方法。返回true说明元素重复就不添加返回false说明元素不重复就添加到集合。不同就直接把元素添加到集合。如果类没有重写hashCode()和equals()默认使用的Object()。一般来说不会相同。而String类重写了hashCode()和equals()方法所以它就可以把内容相同的字符串去掉只留下一个。LinkedHashSet集合类LinkedHashSet集合类继承自HashSetLinkedHashSet底层数据结构由 哈希表和链表组成。特点有序、唯一链表保证元素有序(存储和取出顺序一致)哈希表保证元素的唯一性。TreeSet集合类TreeSet底层数据结构是二叉树。(也称红黑树是一种自平衡的二叉树)。TreeSet底层是TreeMap如何存进去元素的第一个元素存储的时候直接作为根节点存储从第二个元素开始每个元素与根节点进行比较小作为左节点大作为右节点相等不存储如何取出元素分 前序遍历、中序遍历、后序遍历。从根节点开始按照左中右的原则依次取出元素。特点排序、唯一能够对元素按照某种规则进行排序具体取决于使用的构造方法有2种1 自然排序 -- 无参构造真正的比较是依赖于元素的compareTo()这个方法定义在Comparable接口里面的。所以要想重写compareTo()就必须实现Comparable接口这个接口表示的就是自然排序。2 比较器排序 -- 带参构造因为带参构造方法需要传入一个Comparator接口实际要传入的是此接口的实现类的对象。传入此接口实现类的对象可以外部定义一个类实现此接口重写compare()但如果只用一次一般使用匿名内部类在匿名内部类重写compare()TreeSet集合类保证元素排序和唯一的原理排序1 自然排序(是元素具备比较性)让元素所属的类实现自然排序接口Comparable2 比较器排序(是集合具备比较性)让集合的构造方法接收一个比较器接口Comparator的实现类对象唯一是根据比较的返回是否是0来决定的Map集合特点Map集合的最大特点就是它可以存储键值对的元素键和值是映射的关系将键映射到值一个键映射一个值。键唯一但值可以重复。无序。Map集合和Collection集合的区别Map集合存储元素是成对出现的Map集合的键是唯一的值是可重复的Collection集合是单独存储元素的Collection集合的子接口Set是唯一的List是可重复的。Map集合的数据结构只针对键与值无关Collection集合数据结构针对的是元素Map集合功能概述添加功能Vput(K key,V value)添加元素删除功能1 void clear()2 Vremove(Object Key)1 移除所有键值对元素2 根据键删除键值对元素并把值返回判断功能1 booleancontainsKey(Object key)2 booleancontainsValue(Object Value)3 boolean isEmpty()1 判断集合是否存在指定的键2 判断集合是否存在指定的值3 判断集合是否为空获取(遍历)功能1 SetMap.EntryK,VentrySet()2 SetKKeySet()3 Vget(Object key)4 CollectionVvalues()1 返回键值对对象的集合Map.Entry是一个接口此接口有常用的2个方法K getKey()返回与此项对应的键V getValue()返回与此项对应的值2 获取集合中所有的键的集合3 根据键获取值4 获取集合中所有的值的集合长度功能int size()返回集合键值对的对数Map集合的遍历方法1根据键找值思路获取集合中所有的键的集合遍历键的集合得到每一个键根据键获取值。先用KeySet()然后用foreach循环遍历Set集合得到每一个键然后再用get(Object key)。方法2根据键值对对象找键和值思路获取所有键值对对象的集合遍历键值对对象的集合得到每一个键值对对象根据键值对对象得到键和值public class MapDemo { public static void main(String[] args) { MapString, String map new HashMapString, String(); map.put(鹿晗, 关晓彤); map.put(雪儿, 范范); map.put(黄晓明, angelababy); SetEntryString,String set map.entrySet(); for (EntryString,String entry : set) { String key entry.getKey(); String value entry.getValue(); System.out.println(key, value); } } }LinkedHashMap集合类LinkedHashMap集合类继承HashMap。LinkedHashMap底层数据结构由 哈希表和链表组成。特点有序、唯一链表保证元素有序(存储和取出顺序一致)哈希表保证元素的唯一性。TreeMap集合类TreeMap键的数据结构是二叉树。特点排序、唯一HashMap和Hashtable的区别Hashtable是jdk1.0出现的所以它的命名不规范它的底层也是Map集合的哈希表结构用法几乎是一样的HashMap的出现就是用来代替Hashtable的就像ArrayList集合用来代替Vector。Hashtable线程安全的所以效率低。不允许null键null值HashMap线程不安全所以效率高。允许null键null值。Collections——集合操作工具类概述要知道的几个静态方法排序功能public static T void sort(ListT list)默认自然排序二分查找public static T int binarySearch(List? list, T key)最大值public static T T max(Collectin? coll)反转public static T void reverse(ListT list)随机置换public static T void shuffle(ListT list)将一个集合中的所有元素添加到另一个集合中addAll(java.util.Collection? super T c,T... elements)第一个参数就是被添加的集合第二个参数是要向第一个参数集合中要添加进去的元素。第二个参数是一个数组如果第二个参数是集合需要将集合转为数组调用toArray()并且toArray()要指定数组长度