1.1 为什么要使用集合框架?
传统的容器(数组)在进行增、删等破坏性操作时,需要移动元素,可能导致性能问题;同时添加、删除等算法和具体业务耦合在一起,增加了程序开发的复杂度。
Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中
1.2 Collection
Collection是java集合框架(collection-frame)中的顶层接口。
Collection接口表示一个容器,容器中只能存储引用数据类型,建议存同一类型的引用类型,方便后续遍历等操作。
容器中的元素可以是有序的、可重复的,称为List接口
也可能是无序的、唯一的,称为Set接口。
1.2.1 集合常用方法
1 public static void main(String[] args) { 2 3 4 5 /** 6 7 * 增:add/addAll 8 9 * 删:clear/remove/removeAll/retainAll 10 11 * 改: 12 13 * 查:contains/containsAll/isEmpty/size 14 15 */ 16 17 18 19 Collection c1 = new ArrayList(); 20 21 22 23 // 追加 24 25 c1.add("apple"); // Object object = new String("apple"); 26 27 // c1.add(1); // Object object = new Integer(1); 28 29 c1.add("banana"); 30 31 System.out.println(c1); 32 33 34 35 // 追加一个集合 36 37 Collection c2 = new ArrayList(); 38 39 c2.add("java"); 40 41 c2.add("c++"); 42 43 c1.addAll(c2); 44 45 System.out.println(c1); 46 47 48 49 // clear 50 51 //c1.clear(); 52 53 54 55 // c1.remove("apple"); 56 57 // c1.removeAll(c2); 58 59 //c1.retainAll(c2); 60 61 //System.out.println(c1); 62 63 64 65 System.out.println(c1.contains("apple")); 66 67 c2.add("js"); 68 69 System.out.println(c1.containsAll(c2)); 70 71 // c1.clear(); 72 73 System.out.println(c1.isEmpty()); 74 75 // 返回集合元素的个数 76 77 System.out.println(c1.size()); 78 79 80 81 System.out.println(c1.equals(c2)); 82 83 84 85 }
1.2.2 集合的遍历
Iterable 可遍历的接口,集合接口继承于它,集合支持快速遍历。
1 // 快速遍历 2 3 // for-each 4 5 // Object 表示元素类型 6 7 // item表示迭代变量 8 9 // c1表示集合 10 11 for (Object item : c1) { 12 13 System.out.println(item.toString()); 14 15 }
快速遍历的本质
Collection继承Iterable接口,表示集合支持快速遍历。Iterable接口定义了一个方法iterator()用于获取集合的迭代器,是一个Iterator接口类型,iterator()内部返回一个实现类实现类Iterator接口。这个实现类一定具有hasNext和next方法用于判断是否有下一个元素和获取下一个元素。快速遍历就是基于迭代器工作的。
1 public static void main(String[] args) { 2 3 4 5 6 7 Collection c1 = new ArrayList(); 8 9 c1.add("apple"); 10 11 c1.add("banana"); 12 13 c1.add("coco"); 14 15 16 17 18 19 // 快速遍历 20 21 // for-each 22 23 // Object 表示元素类型 24 25 // item表示迭代变量 26 27 // c1表示集合 28 29 for (Object item : c1) { 30 31 System.out.println(item.toString()); 32 33 } 34 35 36 37 // 迭代器遍历(国内) 38 39 Iterator it = c1.iterator(); 40 41 while(it.hasNext()) { 42 43 Object item = it.next(); 44 45 System.out.println(item.toString()); 46 47 } 48 49 50 51 // 国外 52 53 for(Iterator it2=c1.iterator();it2.hasNext();) { 54 55 Object item = it2.next(); 56 57 System.out.println(item.toString()); 58 59 } 60 61 }
1.3 List接口
List 接口中的元素时有序的、可重复的。List接口中的元素通过索引(index)来确定元素的顺序。
有序的 collection(也称为序列)。可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素
1.3.1 List常用方法
1 public static void main(String[] args) { 2 3 4 5 /** 6 7 * 增:add/addAll/add(index,el)/addAll(index,collection) 8 9 * 删:clear/remove/removeAll/remove(index) 10 11 * 改:set(index,el) 12 13 * 查:get(index)/indexOf/lastIndexOf() 14 15 * 其他:contains/containsAll/isEmpty/size 16 17 */ 18 19 List list1 = new ArrayList(); 20 21 // 添加元素 22 23 list1.add("apple"); 24 25 list1.add("banana"); 26 27 // 在指定位置添加元素 28 29 list1.add(0, "coco"); 30 31 32 33 System.out.println(list1); 34 35 36 37 List list2 = new ArrayList(); 38 39 list2.add("java"); 40 41 list2.add("c++"); 42 43 44 45 list1.addAll(1, list2); 46 47 System.out.println(list1); 48 49 50 51 // 删除 52 53 list1.remove(0); 54 55 System.out.println(list1); 56 57 58 59 // 修改 60 61 list1.set(0, "javax"); 62 63 System.out.println(list1); 64 65 66 67 // 查 68 69 System.out.println(list1.get(0)); 70 71 list1.add("apple"); 72 73 list1.add("apple"); 74 75 System.out.println(list1); 76 77 System.out.println(list1.indexOf("apple")); 78 79 System.out.println(list1.lastIndexOf("apple")); 80 81 }
1.3.2 List接口遍历
ListIterator 继承于Iterator,在Iterator的基础上提供了以正向遍历集合,也可以以逆序遍历集合。
hasNext/next 以正向遍历
hasPrevious/previous 以逆序遍历
1 public static void main(String[] args) { 2 3 4 5 6 7 List list1 = new ArrayList(); 8 9 list1.add("apple"); 10 11 list1.add("banana"); 12 13 list1.add("coco"); 14 15 16 17 // 【1】快速遍历 18 19 System.out.println("--for each--"); 20 21 for (Object item : list1) { 22 23 System.out.println(item.toString()); 24 25 } 26 27 28 29 // 【2】普通for 30 31 System.out.println("--for--"); 32 33 for(int i=0;i<list1.size();i++) { 34 35 System.out.println(list1.get(i)); 36 37 } 38 39 40 41 // 【3】集合迭代器 42 43 System.out.println("--iterator--"); 44 45 Iterator it = list1.iterator(); 46 47 while(it.hasNext()) { 48 49 System.out.println(it.next()); 50 51 } 52 53 54 55 System.out.println("--list iterator--"); 56 57 // 正向遍历 58 59 ListIterator it2 = list1.listIterator(); 60 61 while(it2.hasNext()) { 62 63 System.out.println(it2.next()); 64 65 } 66 67 68 69 // 逆序遍历 70 71 while(it2.hasPrevious()) { 72 73 System.out.println(it2.previous()); 74 75 } 76 77 78 79 System.out.println("--list iterator with index--"); 80 81 ListIterator it3 = list1.listIterator(1); 82 83 while(it3.hasNext()) { 84 85 System.out.println(it3.next()); 86 87 } 88 89 }
1.4 数据结构(补充)
数据结构就是数据在内存中存储结构。根据存储的方式不同,分为线性表、二叉树、图、栈、队列等
1.4.1 线性表
线性表数据按照一定的逻辑顺序存储在内存中。线性表是有序的。线性表根据内存的物理结构分为两种:数组和链表
数组是一种逻辑上有序的线性表,物理上也连续。
链表是一种逻辑上有序的线性表,但物理上不连续。
数组和链表的区别
相同点:逻辑上有序的线性表
不同点:
数组在查询时效率高,在添加、删除元素时效率低(涉及移动元素)
链表在查询时效率低(每次从头开始,不能跳跃访问),在添加、删除元素时效率高(不涉及移动元素)
1.4.2 栈
特性:先进后出,后进先出
1.4.3 队列
特性:先进先出
1.5 ArrayList/Vector
ArrayList 是List接口的实现类,底层数据结构是数组,实现大小可变的数组。
ArrayList 线程不安全,jdk1.2
ArrayList 底层数据结构是数组,默认数组大小是10,如果添加的元素个数超过默认容量,ArrayList会自动拓容,拓容原则:newCapacity = oldCapacity + oldCapacity / 2;
如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。
ArrayList作为List接口的实现类,常用方法和遍历方法参考List接口。
Vector 是List接口的实现类,底层数据结构也是数组,也是大小可变的数组。
Vector是线程安全的,jdk1.0
Vector底层数据结构是数组,默认数组大小是10,如果添加的元素个数超过默认容量,Vector会自动拓容,拓容原则:newCapacity = oldCapacity +capacityIncrement(增长因子);如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。
注意:Vector 在实现List接口的同时,同添加了自身特有的方法xxxElement,未来使用时为了程序的可拓展性,一定要按照接口来操作Vector。
1.6 LinkedList
LinkedList是List接口的实现类,底层数据结构是链表。
LinekList常用方法和遍历方法参照List接口。
LinkedList 线程不安全
除了实现List接口, 还实现栈接口
push入栈操作 / pop出栈操作
1 public class Test01 { 2 3 public static void main(String[] args) { 4 5 LinkedList list = new LinkedList(); 6 7 list.push("apple"); 8 9 list.push("banana"); 10 11 list.push("coco"); 12 13 14 15 16 17 System.out.println(list.pop()); 18 19 System.out.println(list.pop()); 20 21 System.out.println(list.pop()); 22 23 24 25 // java.util.NoSuchElementException 26 27 System.out.println(list.pop()); 28 29 } 30 31 }
队列(Queue)接口
add/remove/element() 可能会出现NoSuchElementException异常
1 public static void main(String[] args) { 2 3 4 5 LinkedList queue = new LinkedList(); 6 7 // 入队 8 9 /** 10 11 * 队列头 队列尾 12 13 *<----- <----- 14 15 * [apple, banana, coco] 16 17 */ 18 19 queue.add("apple"); 20 21 queue.add("banana"); 22 23 queue.add("coco"); 24 25 System.out.println(queue); 26 27 28 29 // 出队 30 31 System.out.println(queue.remove()); 32 33 System.out.println(queue.remove()); 34 35 System.out.println(queue.remove()); 36 37 System.out.println(queue); 38 39 40 41 // java.util.NoSuchElementException 42 43 System.out.println(queue.remove()); 44 45 46 47 48 49 // 获取表头元素 50 51 System.out.println(queue.element()); 52 53 }
offer/poll/peek 可能会返回特殊值(null)
1 public static void main(String[] args) { 2 3 4 5 LinkedList queue = new LinkedList(); 6 7 // 入队 8 9 /** 10 11 * 队列头 队列尾 12 13 *<----- <----- 14 15 * [apple, banana, coco] 16 17 */ 18 19 queue.offer("apple"); 20 21 queue.offer("banana"); 22 23 queue.offer("coco"); 24 25 26 27 // 出队列 28 29 //System.out.println(queue.poll()); 30 31 //System.out.println(queue.poll()); 32 33 //System.out.println(queue.poll()); 34 35 System.out.println(queue); 36 37 38 39 //System.out.println(queue.poll()); 40 41 42 43 // 获取表头元素 44 45 System.out.println(queue.peek()); 46 47 48 49 }
双向队列(Deque)接口
1 /** 2 3 * 以双向队列形式操作LinkedList 4 5 */ 6 7 public class Test04 { 8 9 public static void main(String[] args) { 10 11 12 13 LinkedList queue = new LinkedList(); 14 15 // 入队 16 17 /** 18 19 *<----- <----- 20 21 * [apple, banana, coco] 22 23 * ----> -----> 24 25 */ 26 27 28 29 queue.addFirst("apple"); 30 31 queue.addFirst("banana"); 32 33 queue.addFirst("coco"); 34 35 System.out.println(queue); 36 37 38 39 System.out.println(queue.removeLast()); 40 41 System.out.println(queue.removeFirst()); 42 43 System.out.println(queue.removeFirst()); 44 45 System.out.println(queue); 46 47 48 49 // 获取头元素 50 51 System.out.println(queue.getFirst()); 52 53 54 55 } 56 57 }
1.7 Iterator和ListIterator
Iterator在迭代过程中不允许向集合中添加元素
1 public static void main(String[] args) { 2 3 ArrayList list = new ArrayList(); 4 5 list.add("apple"); 6 7 list.add("banana"); 8 9 list.add("coco"); 10 11 12 13 Iterator it = list.iterator(); 14 15 while(it.hasNext()) { 16 17 String item = (String) it.next(); 18 19 if(item.equals("banana")) { 20 21 list.add("test"); 22 23 } 24 25 } 26 27 28 29 System.out.println(list); 30 31 }
当通过Iterator集合迭代器遍历集合过程中,不能再向集合汇总添加元素,否则出现ConcurrentModificationException 并发修改异常。
ListIterator允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置
1 public class Test01 { 2 3 public static void main(String[] args) { 4 5 ArrayList list = new ArrayList(); 6 7 list.add("apple"); 8 9 list.add("banana"); 10 11 list.add("coco"); 12 13 14 15 ListIterator it = list.listIterator(); 16 17 while(it.hasNext()) { 18 19 String item = (String) it.next(); 20 21 if(item.equals("banana")) { 22 23 it.add("test"); 24 25 } 26 27 } 28 29 30 31 System.out.println(list); 32 33 } 34 35 }