集合(上)
2019-05-04 09:39:41来源:博客园 阅读 ()
传统的容器(数组)在进行增、删等破坏性操作时,需要移动元素,可能导致性能问题;同时添加、删除等算法和具体业务耦合在一起,增加了程序开发的复杂度。Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中。
1 Collection 接口
Collection是java集合框架(collection-frame)中的顶层接口。Collection接口是一个容器,容器中只能存储引用数据类型,建议存同一类型的引用类型,方便后续遍历等操作。容器中的元素可以是有序的、可重复的,称为List接口;也可能是无序的、唯一的,称为Set接口。
1.1 集合常用方法
1 public static void main(String[] args) { 2 3 /** 4 * 增:add/addAll 5 * 删:clear/remove/removeAll/retainAll 6 * 改: 7 * 查:contains/containsAll/isEmpty/size 8 */ 9 10 Collection c1 = new ArrayList(); 11 12 // 追加 13 c1.add("apple"); // Object object = new String("apple"); 14 // c1.add(1); // Object object = new Integer(1); 15 c1.add("banana"); 16 System.out.println(c1); 17 18 // 追加一个集合 19 Collection c2 = new ArrayList(); 20 c2.add("java"); 21 c2.add("c++"); 22 c1.addAll(c2); 23 System.out.println(c1); 24 25 System.out.println(c1.contains("apple")); 26 c2.add("js"); 27 System.out.println(c1.containsAll(c2)); 28 // c1.clear(); 29 System.out.println(c1.isEmpty()); 30 // 返回集合元素的个数 31 System.out.println(c1.size()); 32 33 System.out.println(c1.equals(c2)); 34 }
1.3 集合的遍历
Iterable 是一个可遍历的接口,集合接口继承于它,集合支持快速遍历。
for (Object item : c1) { System.out.println(item.toString()); }
快速遍历的本质:
Collection 继承 Iterable 接口,表示集合支持快速遍历。Iterable 接口定义了一个方法iterator()用于获取集合的迭代器,是一个 Iterator 接口类型,iterator()内部返回一个实现类实现类 Iterator 接口。这个实现类一定具有 hasNext 和 next 方法用于判断是否有下一个元素和获取下一个元素。快速遍历就是基于迭代器工作的。
1 public static void main(String[] args) { 2 3 Collection c1 = new ArrayList(); 4 c1.add("apple"); 5 c1.add("banana"); 6 c1.add("coco"); 7 8 // 快速遍历 9 // for-each 10 // Object 表示元素类型 11 // item表示迭代变量 12 // c1表示集合 13 for (Object item : c1) { 14 System.out.println(item.toString()); 15 } 16 17 // 方法1 18 Iterator it = c1.iterator(); 19 while(it.hasNext()) { 20 Object item = it.next(); 21 System.out.println(item.toString()); 22 } 23 24 // 方法2(推荐) 25 for(Iterator it2=c1.iterator();it2.hasNext();) { 26 Object item = it2.next(); 27 System.out.println(item.toString()); 28 } 29 }
查看源码可以看到 next 方法的实现
1 @SuppressWarnings("unchecked") 2 public E next() { 3 checkForComodification(); 4 int i = cursor; 5 if (i >= size) 6 throw new NoSuchElementException(); 7 Object[] elementData = ArrayList.this.elementData; 8 if (i >= elementData.length) 9 throw new ConcurrentModificationException(); 10 cursor = i + 1; 11 return (E) elementData[lastRet = i]; 12 }
iterator 方法会返回一个私有类 Itr 的实例,该类中定义了一个 cursor 变量,初始值为 0,表示当前”光标“指向的元素索引;定义了一个 lastRet 变量初始值为 -1,表示上一个遍历过的元素的索引。每次使用 next 后,将 cursor 赋给 i ,光标 cursor 后移一位,同时返回当前 i 指向的元素,并将 i 赋给 lastRet。
iterator 是线程不安全的,不支持在遍历的同时修改集合元素。每次使用 next 的时候,会首先使用 checkForComodification 方法,查看源码可知,该方法会判断两个变量 modcount、expectedModCount 是否相等,如果不相等就抛出“同时修改异常”。modcount 是 ArrayList 的父类 AbstractList 中定义的一个变量,Arraylist 的 add 方法每次执行时,会先调用 ensureCapacityInternal 方法判断容量并自动扩容,该方法又调用了 ensureExplicitCapacity 方法,该方法每次调用时 modcount 会自加一次。而expectedMoCount 是在创建 Itr 实例时生成的,将ArrayList 的 modcount 赋给它,所以当在遍历过程中修改集合元素,next 方法调用时就会抛出“同时修改异常”。
1 private void ensureCapacityInternal(int minCapacity) { 2 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); 3 } 4 5 private void ensureExplicitCapacity(int minCapacity) { 6 modCount++; 7 8 // overflow-conscious code 9 if (minCapacity - elementData.length > 0) 10 grow(minCapacity); 11 }
2 List 接口
List 继承 Collection。List 接口中的元素时有序的、可重复的。List接口中的元素通过索引(index)来确定元素的顺序。可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
2.1 List 常用方法
1 public static void main(String[] args) { 2 3 /** 4 * 增:add/addAll/add(index,el)/addAll(index,collection) 5 * 删:clear/remove/removeAll/remove(index) 6 * 改:set(index,el) 7 * 查:get(index)/indexOf/lastIndexOf() 8 * 其他:contains/containsAll/isEmpty/size 9 */ 10 List list1 = new ArrayList(); 11 // 添加元素 12 list1.add("apple"); 13 list1.add("banana"); 14 // 在指定位置添加元素 15 list1.add(0, "coco"); 16 17 System.out.println(list1); 18 19 List list2 = new ArrayList(); 20 list2.add("java"); 21 list2.add("c++"); 22 23 list1.addAll(1, list2); 24 System.out.println(list1); 25 26 // 删除 27 list1.remove(0); 28 System.out.println(list1); 29 30 // 修改 31 list1.set(0, "javax"); 32 System.out.println(list1); 33 34 // 查 35 System.out.println(list1.get(0)); 36 list1.add("apple"); 37 list1.add("apple"); 38 System.out.println(list1); 39 System.out.println(list1.indexOf("apple")); 40 System.out.println(list1.lastIndexOf("apple")); 41 }
2.2 List 接口的遍历
ListIterator 继承于Iterator,在Iterator的基础上提供了以正向遍历集合,也可以以逆序遍历集合。hasNext/next 以正向遍历;hasPrevious/previous 以逆序遍历。
1 public static void main(String[] args) { 2 3 List list1 = new ArrayList(); 4 list1.add("apple"); 5 list1.add("banana"); 6 list1.add("coco"); 7 8 // 【1】快速遍历 9 System.out.println("--for each--"); 10 for (Object item : list1) { 11 System.out.println(item.toString()); 12 } 13 14 // 【2】普通for 15 System.out.println("--for--"); 16 for(int i=0;i<list1.size();i++) { 17 System.out.println(list1.get(i)); 18 } 19 20 // 【3】集合迭代器 21 System.out.println("--iterator--"); 22 Iterator it = list1.iterator(); 23 while(it.hasNext()) { 24 System.out.println(it.next()); 25 } 26 27 System.out.println("--list iterator--"); 28 // 正向遍历 29 ListIterator it2 = list1.listIterator(); 30 while(it2.hasNext()) { 31 System.out.println(it2.next()); 32 } 33 34 // 逆序遍历 35 while(it2.hasPrevious()) { 36 System.out.println(it2.previous()); 37 } 38 39 System.out.println("--list iterator with index--"); 40 ListIterator it3 = list1.listIterator(1); 41 while(it3.hasNext()) { 42 System.out.println(it3.next()); 43 } 44 }
3 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。
4 LinkedList
LinkedList是List接口的实现类,底层数据结构是链表。LinekList常用方法和遍历方法参照List接口。LinkedList 线程不安全。
除了实现List接口, 还实现了栈接口(后进先出 LIFO),通过 push 和 pop 方法实现栈的操作。
1 public static void main(String[] args) { 2 LinkedList list = new LinkedList(); 3 list.push("apple"); 4 list.push("banana"); 5 list.push("coco"); 6 7 8 System.out.println(list.pop()); //coco 9 System.out.println(list.pop()); //banana 10 System.out.println(list.pop()); 11 12 // java.util.NoSuchElementException 13 System.out.println(list.pop()); 14 }
也实现了队列接口(先进先出 FIFO),提供两套方法实现。
add/remove/element() 可能会出现NoSuchElementException异常
1 public static void main(String[] args) { 2 3 LinkedList queue = new LinkedList(); 4 // 入队 5 /** 6 * 队列头 队列尾 7 *<----- <----- 8 * [apple, banana, coco] 9 */ 10 queue.add("apple"); 11 queue.add("banana"); 12 queue.add("coco"); 13 System.out.println(queue); 14 15 // 出队 16 System.out.println(queue.remove()); //返回队列头元素,并从队列中移除 17 System.out.println(queue.remove()); 18 System.out.println(queue.remove()); 19 System.out.println(queue); 20 21 // java.util.NoSuchElementException 22 System.out.println(queue.remove()); 23 24 25 // 获取表头元素 26 System.out.println(queue.element()); 27 }
offer/poll/peek 可能会返回特殊值(null)
1 public static void main(String[] args) { 2 3 LinkedList queue = new LinkedList(); 4 // 入队 5 /** 6 * 队列头 队列尾 7 *<----- <----- 8 * [apple, banana, coco] 9 */ 10 queue.offer("apple"); 11 queue.offer("banana"); 12 queue.offer("coco"); 13 14 // 出队列 15 //System.out.println(queue.poll()); 16 //System.out.println(queue.poll()); 17 //System.out.println(queue.poll()); 18 System.out.println(queue); 19 20 //System.out.println(queue.poll());//输出 null 21 22 // 获取表头元素 23 System.out.println(queue.peek()); 24 25 }
同时也继承了双向队列接口,两头可进可出,一样提供两套方法,一个会抛异常,一个会返回 null。
用法如上面代码类似。
5 ListIterator
正如上文讲遍历时所说的,Iterator 不支持遍历的过程中修改集合元素,而 ListIterator 正好弥补了这个缺陷,ArrayList 对象可以使用 listIterator 方法获得一个 ListIterator 的实例。ListIterator允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。
1 public static void main(String[] args) { 2 ArrayList list = new ArrayList(); 3 list.add("apple"); 4 list.add("banana"); 5 list.add("coco"); 6 7 ListIterator it = list.listIterator(); 8 while(it.hasNext()) { 9 String item = (String) it.next(); 10 if(item.equals("banana")) { 11 it.add("test"); 12 //在当前光标(cursor)位置插入一个元素,这个 add 方法是属于ListIterator 的 13 // next 方法调用后,光标 cursor 会后移 14 } 15 } 16 17 System.out.println(list); //[apple, banana, test, coco] 18 }
6 泛型(generic)
6.1 概念
泛型允许开发者在强类型程序设计语言(java)编写代码时定义一些可变部分,这些部分在使用前必须作出指明。泛型就是将类型参数化。
- ArrayList<E> list表示声明了一个列表list,列表的元素是E类型
- ArrayList<String> list = new ArrayList<String>();声明了一个列表list,列表的元素只能是String类型。
6.2 泛型的擦除
泛型在编译器起作用,运行时jvm察觉不到泛型的存在。泛型在运行时已经被擦除了。
1 public static void main(String[] args) { 2 ArrayList<String> list = new ArrayList<String>(); 3 list.add("apple"); 4 System.out.println(list instanceof ArrayList); //true 5 System.out.println(list instanceof ArrayList<String>); 6 //Cannot perform instanceof check against parameterized type ArrayList<String>. 7 // Use the form ArrayList<?> instead since further generic type information will 8 //be erased at runtime 9 }
6.3 泛型的应用
泛型类
当一个类中属性的数据类型不确定时,具体是什么类型由使用者来确定时,使用泛型。泛型类的形式:
1 public class 类名<T> { 2 3 }
定义一个泛型类:
1 class FanClass<T> { 2 private T t; 3 4 public T getT() { 5 return t; 6 } 7 8 public void setT(T t) { 9 this.t = t; 10 } 11 12 public FanClass(T t) { 13 super(); 14 this.t = t; 15 } 16 17 public FanClass() { 18 super(); 19 } 20 } 21 public class Test01 { 22 public static void main(String[] args) { 23 FanClass<String> fan = new FanClass<String>(); 24 fan.setT("apple"); 25 26 FanClass<Integer> fan2 = new FanClass<Integer>(); 27 fan2.setT(1); 28 } 29 }
泛型方法
当一个方法的参数类型不确定时,具体是什么类型由使用者来确定,可以考虑使用泛型方法,泛型方法在调用时确定(指明)类型。形式:
1 public <T> void xxx(T a) { 2 System.out.println(a); 3 }
举例:
1 class Student { 2 public <T> void showInfo(T a) { 3 System.out.println(a); 4 } 5 } 6 public class test { 7 public static void main(String[] args) { 8 Student stu = new Student(); 9 stu.showInfo(1); 10 stu.showInfo("apple"); 11 stu.showInfo(1.0f); //传入的参数是什么类型,T 就是什么类型 12 } 13 }
原文链接:https://www.cnblogs.com/carlosouyang/p/10804178.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:Java 锁
下一篇:Spring Cloud是什么?
- 与JAVA集合相遇 2020-06-11
- 掌握SpringBoot-2.3的容器探针:实战篇 2020-06-11
- Java笔记:集合 2020-06-10
- 2020最新IDEA插件大集合,一款能帮助你写代码的工具是多么重 2020-06-09
- Java电子书高清PDF集合免费下载 2020-06-05
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash