赞
踩
Java 集合框架提供了一套性能优良,使用方便的接口和类,其位于 java.util 包中, 所以当使用集合框架的时候需要进行导包。
Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合;另一种是图(Map),存储键/值对映射。
说明:
(1)Collection 接口有两个重要的子接口 List、 Set , 他们的实现子类都是单列集合。
(2)Map 接口的实现子类是双列集合,存放的 K-V(键值对)
如下表:
接口 | 描述 |
---|---|
Collection 接口 | Collection 是最基本的集合接口,一个 Collection 代表一组 Object(即 Collection 的元素), Java不提供直接继承自 Collection的类,只提供继承自 Collection 接口的子接口(如 List和 set)。Collection 接口存储一组不唯一,无序的对象(不能通过索引来访问 Collection 集合中的对象)。 |
List 接口 | List 接口继承自 Collection 接口 ,但 List 接口 是一个有序的集合,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(即元素在 List 中的位置,类似于数组的下标)来访问 List 集合中的元素,第一个元素的索引为 0。而且 List 集合中允许有相同的元素。可以说,List 接口的集合存储一组不唯一,有序(插入顺序)的对象。 |
Set 接口 | Set 接口继承自 Collection 接口,具有与 Collection 完全一样的接口,只是方法上有部分不同,和 Collection 接口 相同,Set 接口存储一组唯一,无序的对象。 |
Map 接口 | Map 接口与 Collection 接口同级(彼此没有继承关系),Map 图存储一组 键-值 对象,提供key(键)到value(值)的映射。 |
Set 和 List 接口的区别:
(1)Set 接口集合存储的是无序的,不重复的数据。List 接口集合存储的是有序的,可以重复的元素。
(2)Set 集合 底层使用的是 链表数据结构,其检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 (实现子类有 HashSet , TreeSet 等)。
(3)List 结合 底层和数组类似,但是它可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。其检索元素效率高,插入和删除效率低,插入和删除会引起其他元素位置改变 (实现子类有 ArrayList , LinkedList , Vector 等)。
Java 提供了一套实现了 Collection 接口的标准集合类。其中一些是具体类,这些类可以直接拿来使用,而另外一些是抽象类,提供了接口的部分实现。
如下表:
类名 | 描述 |
---|---|
ArrayList 类 | 该类实现了 List 接口,允许存储 null(空值)元素,且可存储重复元素。该类实现了可变大小的数组,随机访问和遍历元素时,提供了更好的性能。该类是非同步的, 在多线程的情况下不要使用。ArrayList 类在扩容时会扩容当前容量的1.5倍。 |
Vector 类 | 该类和 ArrayList 类非常相似,但该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。 |
LinkedList 类 | 该类实现了 List 接口,允许存储 null(空值)元素,且可存储重复元素,主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个 LinkedList,则必须自己实现访问同步,解决方法就是在创建 LinkedList 类 时候再构造一个同步的 LinkedList 。 |
HashSet 类 | 该类实现了 Set 接口,不允许存储重复元素,并且不保证集合中元素的顺序,其允许存储 null (空值)元素,但最多只能存储一个。 |
TreeSet 类 | 该类实现了 Set 接口,不允许存储重复元素,并且不保证集合中元素的顺序,其允许存储 null (空值)元素,但最多只能存储一个。该类可以实现排序等功能。 |
如下表:
类名 | 描述 |
---|---|
HashMap 类 | HashMap 类是一个散列表,它存储的内容是键-值对 (key-value) 映射。该类实现了 Map 接口,根据键的 HashCode 值存储元素,具有很快的访问速度,但最多允许一个元素的键为 null (空值),它不支持线程同步。 |
TreeMap 类 | TreeMap 类继承了AbstractMap ,实现了大部分 Map 接口,并且使用一颗树。 |
HashTable 类 | Hashtable 继承自 Dictionary(字典) 类,用来存储 键-值对。 |
Properties 类 | Properties 继承自 HashTable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。 |
特此说明:由于集合框架的内容繁多,因此本文只介绍 Collection 集合下的 List 接口及其重要实现子类的内容,其余集合框架的知识将会在下篇博文分享。 |
import java.util.ArrayList; import java.util.List; public class CollectionMethod { public static void main(String[] args) { // 以实现了Collection 接口 的子类 ArrayList 来举例; ArrayList list = new ArrayList(); // 1. add:添加单个元素 list.add("jack"); list.add(10);// 底层自动装箱:list.add(new Integer(10)) list.add(true);// 同上 System.out.println("list=" + list);// [jack, 10, true] // 2. addAll:添加多个元素 ArrayList list2 = new ArrayList();// 创建一个新的集合 list2.add("红楼梦"); list2.add("三国演义"); list.addAll(list2); System.out.println("list=" + list);// [jack, 10, true, 红楼梦, 三国演义] // 3. remove:删除指定元素,如果不指定则默认删除第一个元素 list.remove(true);// 指定删除某个元素 System.out.println("list=" + list);// [jack, 10, 红楼梦, 三国演义] // 4. removeAll:删除多个元素 list.add("聊斋"); list.removeAll(list2); System.out.println("list=" + list);// [jack, 10, 聊斋] // 5. contains:查找元素是否存在,返回 boolean 值 System.out.println(list.contains("jack"));// T // 6. containsAll:查找多个元素是否都存在,返回 boolean 值 System.out.println(list.containsAll(list2));// T // 7. size:获取元素个数 System.out.println(list.size());// 3 // 8. isEmpty:判断是否为空 System.out.println(list.isEmpty());// F // 9. clear:清空 list.clear(); System.out.println("list=" + list);// [] } }
Iterator(迭代器)不是一个集合,它是一种用于访问 Collection 集合的接口,主要用于遍历 Collection 集合中的元素,所有实现了 Collection 接口的子类集合都可以使用迭代器。
迭代器的执行原理:
- 迭代器就相当于一个游标,初始时指向集合中的第1个元素的的前一个位置;
- 首先使用 hasNext() 方法来判断迭代器的下一个位置是否还有元素,
- 若下一个位置有元素则使用 next() 方法返回下一个元素,并将迭代器的位置向后移一位。
- 若没有,则不调用 next() 方法,直接退出迭代器。
迭代器常用方法:
(1)调用 coll.next() 会返回迭代器的下一个元素,并且更新迭代器的状态(迭代器下移)。
(2)调用 coll.hasNext() 判断集合中是否还有下一个元素。
(3)调用 coll.remove() 将迭代器返回的元素删除。
注意:在使用 next() 方法前必须使用 hasNext() 方法判断集合中是否还有下一个元素,否则可能会出现异常。
代码演示:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class CollectionIterator { public static void main(String[] args) { List list = new ArrayList();// 创建一个新的集合,该集合实现了 Collection 接口 list.add(1); list.add(2); list.add(3); // 1. 先得到 list集合 对应的 迭代器 // 使用集合的 iterator() 方法来获得该集合的迭代器; // 所有实现了 Collection 接口的子类都拥有 iterator() 方法 Iterator iterator = list.iterator(); // 2. 使用 while 循环 + 迭代器 遍历集合 // 首先判断集合中(下一个位置)是否还有元素 while (iterator.hasNext()) { // 若有,则获取下一个位置的元素,迭代器位置向下移一位 Object obj = iterator.next(); // 输出该元素 System.out.println("obj=" + obj); } // 3. 注意:当退出 while 循环后 , 这时 iterator 迭代器,指向的是集合中的最后一个元素; // 若再次 使用 iterator.next(); 会产生 NoSuchElementException 异常,因为集合中下一个位置没有元素存在了; // 4. 如果希望再次遍历集合,需要重置 迭代器的状态; iterator = list.iterator();// 重置迭代器 System.out.println("===第二次遍历==="); while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println("obj=" + obj); } } }
(1)使用迭代器遍历(所有集合中都可以使用 迭代器 来遍历)。
(2)使用普通 for 循环遍历(普通 for 循环 是通过元素的索引来获取元素,在无序的集合中不能此方式来获取元素。比如 实现了 Set 接口的子类集合)。
(3)使用增强 for 循环遍历(增强 for 循环的底层其实是实现了 迭代器,因此在 所有集合中都可以使用该方式遍历)。
import java.util.*; public class ListFor { public static void main(String[] args) { ArrayList list = new ArrayList();// 创建一个有序的集合 list.add("jack"); list.add("tom"); list.add("鱼香肉丝"); list.add("北京烤鸭子"); //遍历方式: //1. 迭代器 Iterator iterator = list.iterator(); while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println(obj); } System.out.println("=====增强for====="); //2. 增强 for 循环 for (Object o : list) { System.out.println("o=" + o); } System.out.println("=====普通for===="); //3. 普通 for 循环 for (int i = 0; i < list.size(); i++) { System.out.println("对象=" + list.get(i)); } } }
(1)List 接口是继承了 Collection 接口的子接口,它是一个有序的集合,使用此接口能够精确的控制每个元素插入的位置。
(2)能够通过索引(元素在List中位置,类似于数组的下标)来访问 List 集合中的元素,第一个元素的索引为 0,而且允许有相同的元素。
(3)List 集合存储一组不唯一,有序(插入顺序)的对象/元素。
List 接口的常用方法:(以其子类 ArrayList 举例)
import java.util.ArrayList; import java.util.List; public class ListMethod { public static void main(String[] args) { List list = new ArrayList();// 向上转型 list.add("张三丰");// 直接添加元素 list.add("贾宝玉"); // 1. void add(int index, Object ele):在 index 位置插入 ele元素 list.add(1, "韩顺平");// 在 index = 1的位置插入一个对象 System.out.println("list=" + list);// [张三丰, 韩顺平, 贾宝玉] // 2. boolean addAll(int index, Collection eles):从 index位置开始将 eles中的所有元素添加进来 List list2 = new ArrayList();// 创建一个新的集合 list2.add("jack"); list2.add("tom"); list.addAll(1, list2);// 从第 1个位置开始添加 System.out.println("list=" + list);// [张三丰, jack, tom, 韩顺平, 贾宝玉] // 3. Object get(int index):获取指定 index位置的元素 Object obj = list.get(1);// 返回了第 2个元素, jack // 4. int indexOf(Object obj):返回 obj在集合中首次出现的位置 System.out.println(list.indexOf("tom"));// 2 // 5. int lastIndexOf(Object obj):返回 obj在当前集合中末次出现的位置 list.add("韩顺平"); System.out.println("list=" + list); System.out.println(list.lastIndexOf("韩顺平"));// 3 // 6. Object remove(int index):移除指定 index位置的元素,并返回此元素 Object obj1 = list.remove(0);// 返回了第一个元素 System.out.println("list=" + list);// [jack, tom, 韩顺平, 贾宝玉] // 7. Object set(int index, Object ele):设置指定 index位置的元素为 ele , 相当于是替换 list.set(1, "玛丽"); System.out.println("list=" + list);// [jack, 玛丽, 韩顺平, 贾宝玉] // 8.List subList(int fromIndex, int toIndex):返回从 fromIndex到 toIndex位置的子集合 // 注意返回的子集合范围要满足: fromIndex <= subList < toIndex,否则会出现异常 List returnlist = list.subList(0, 2); System.out.println("returnlist=" + returnlist);// [jack, 玛丽, 韩顺平] } }
(1)ArrayList 类中维护了一个 Object 类型的数组 elementData。
transient Object elementData ;// transient: 表示瞬间, 短暂的, 代表该属性不会被序列化
(2)当创建 ArrayList 对象时, 如果使用的是无参构造器, 则初始 elementData 容量为 0;当第 1 次添加元素时, 会动态地将集合容量 elementData 扩充为 10 , 如需再次扩容, 则默认扩容为当前 elementData 的1.5倍。
(3)如果使用的是指定容量大小的构造器, 则初始 elementData 容量为指定值, 如果再次需要扩容, 则直接扩充为当前 elementData 的1.5倍。
import java.util.Vector; public class Vector_ { public static void main(String[] args) { // 无参构造器 // Vector vector = new Vector(); // 有参数的构造器 Vector vector = new Vector(8); for (int i = 0; i < 10; i++) { vector.add(i); } vector.add(100); System.out.println("vector=" + vector); 源码分析: 1. Vector vector = new Vector() : 调用vector 的无参构造器 // 无参构造器的底层还是调用了有参构造器,默认传入容量为 10 public Vector() { this(10); } // Vector vector = new Vector(8) : 使用带参构造器 public Vector(int initialCapacity) { this(initialCapacity, 0); } 2. vector.add(i) : 调用 add() 方法添加元素; 2.1 // 下面这个方法添加数据到 vector集合 public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } 2.2 // 确定是否需要扩容条件 : minCapacity - elementData.length > 0 private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 2.3 // 如果需要的数组容量不够用,就扩容 , 扩容的算法: newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); // 其实就是扩容两倍 private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } } }
import java.util.Iterator; import java.util.LinkedList; public class LinkedListCRUD { public static void main(String[] args) { LinkedList linkedList = new LinkedList(); // 1.增加结点 linkedList.add(1); linkedList.add(2); linkedList.add(3); System.out.println("linkedList=" + linkedList); // 2.删除结点 linkedList.remove(); // 这里默认删除的是第一个结点 linkedList.remove(2);// 删除指定位置的结点 System.out.println("linkedList=" + linkedList); // 3.修改某个结点对象 linkedList.set(1, 999); System.out.println("linkedList=" + linkedList); // 4.得到某个结点对象 // get(1) 是得到双向链表的第二个对象 Object o = linkedList.get(1); System.out.println(o);// 999 // 因为 LinkedList 实现了 List接口, 可以使用迭代器遍历 System.out.println("====LinkeList遍历迭代器===="); Iterator iterator = linkedList.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println("next=" + next); } } }
public class LinkedList01 { public static void main(String[] args) { // 模拟一个简单的双向链表 Node jack = new Node("jack"); Node tom = new Node("tom"); Node hsp = new Node("老韩"); // 连接三个结点,形成双向链表 // jack -> tom -> hsp jack.next = tom; tom.next = hsp; // hsp -> tom -> jack hsp.pre = tom; tom.pre = jack; Node first = jack;// 让 first 引用指向 jack,就是双向链表的头结点 Node last = hsp; // 让 last 引用指向 hsp,就是双向链表的尾结点 // 演示,从头到尾进行遍历 System.out.println("===从头到尾进行遍历==="); while (true) { if(first == null) { break; } // 输出 first 信息 System.out.println(first); first = first.next; } // 演示,从尾到头的遍历 System.out.println("====从尾到头的遍历===="); while (true) { if(last == null) { break; } // 输出 last 信息 System.out.println(last); last = last.pre; } // 演示链表的添加对象/数据,很方便 // 要求,在 tom 直接插入一个对象 smith // 1. 先创建一个 Node 结点,name 就是 smith Node smith = new Node("smith"); // 下面就把 smith 加入到双向链表了 smith.next = hsp; smith.pre = tom; hsp.pre = smith; tom.next = smith; // 让first 再次指向 jack first = jack;// 让 first 引用指向 ack,就是双向链表的头结点 System.out.println("===从头到尾进行遍历==="); while (true) { if(first == null) { break; } // 输出 first 信息 System.out.println(first); first = first.next; } last = hsp; // 让last 重新指向最后一个结点 // 演示,从尾到头的遍历 System.out.println("====从尾到头的遍历===="); while (true) { if(last == null) { break; } // 输出 last 信息 System.out.println(last); last = last.pre; } } } // 定义一个Node 类,一个 Node 对象表示双向链表的一个结点 class Node { public Object item; // 真正存放数据 public Node next; // 指向后一个结点 public Node pre; // 指向前一个结点 public Node(Object name) { this.item = name; } public String toString() { return "Node name=" + item; } }
在简单了解了双向链表的结构后,我们进一步分析 LinkedList 集合添加和删除元素的底层机制源码。
源码分析如下:
一、add 源码阅读:linkedList.add(1); 1. 使用无参构造器,创建一个集合:public LinkedList() {} LinkedList linkedList = new LinkedList(); 2. 这时 linkeList 的属性 first = null, last = null 3. 执行 add 方法 public boolean add(E e) { linkLast(e); return true; } 4. 将新的结点,直接加入到双向链表的最后 void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; } 二、删除源码阅读: linkedList.remove(); // 不传入参数则默认删除的是第一个结点 1. 执行 removeFirst 方法 public E remove() { return removeFirst(); } 2. 执行 removeFirst()方法 public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } 3. 执行 unlinkFirst 方法, 将 f 指向的双向链表的第一个结点拿掉 private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。