什么是集合
简单来讲:集合就是一个放数据的容器,准确的说是放引用数据的容器。
为什么要使用集合框架?
传统的容器(数组)在进行增、删等破坏性操作时,需要移动元素,可能导致性能问题;同时添加、删除等算法和具体业务耦合在一起,增加了程序开发的复杂度。
Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中
package cn.collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /**Collection是java集合框架(collection-frame)中的顶层接口。 Collection接口是一个容器,容器中只能存储引用数据类型,建议存同一类型的引用类型,方便后续遍历等操作。 容器中的元素可以是有序的、可重复的,称为List接口 也可能是无序的、唯一的,称为Set接口。 * * * */ public class TestCollection { public static void main(String[] args) { /**通过实现类来实现 * 增:add/addAll * 删:clear/remove/removeAll/retainAll * 改: * 查:contains/containsAll/isEmpty/size */ Collection c1=new ArrayList();//通过ArrayList类(本质上是List接口的实现类)实现Collection接口 //通过ArrayList类把引用数据追加给Collection容器 c1.add("apple"); // Object object = new String("apple"); c1.add("banana");//把引用数据类型加入Collection容器 System.out.println(c1);//输出结果:[apple, banana] //基本数据类型通过包装类存入Collection // c1.add(1); // Object object = new Integer(1); // 追加一个集合 Collection c2 = new ArrayList(); c2.add("java");//把引用数据类型加入Collection容器 c2.add("c+"); c2.add("c"); c2.add("c++"); c2.add("a++"); c2.add("d++"); c1.addAll(c2);//可以直接把一个集合追加给另一个相同类型的集合 System.out.println(c1);//输出结果:[apple, banana, java, c+, c, c++, a++, d++] Collection c3=new ArrayList(); c3.add("d++"); c3.add("ff++"); //删除列表中的所有元素。 //c1.clear(); System.out.println(c1);//输出结果:[] // 删除此列表中首次出现的指定元素(如果存在,从左往右)。 c2.remove("c++"); System.out.println(c2);//输出结果:[java, c+, c, a++, d++] //删除c1中的和c3有同样的元素(删除两个容器中同样的元素) c1.removeAll(c3); System.out.println(c1);//输出结果:[apple, banana, java, c+, c, c++, a++] //提取两个容器中相同的元素,然后清空c1,再把相同元素追加给c1 c1.retainAll(c2); System.out.println(c1);//输出结果:[java, c+, c, a++] //如果此列表中包含指定的元素,则返回 true。 System.out.println(c1.contains("apple"));//输出结果:false System.out.println(c2);//输出结果:[java, c+, c, a++, d++] c1.add("d++"); //c1和c2是否相等 System.out.println(c1.containsAll(c2));//输出结果:true //c1是否为空 //c1.clear();//清空c1//输出结果:true System.out.println(c1.isEmpty());//输出结果:false // 返回集合元素的个数 System.out.println(c1.size());//输出结果:5 //c1和c2内元素是否相等 System.out.println(c1.equals(c2));//输出结果:true Collection c4 = new ArrayList(); c4.add("apple"); c4.add("banana"); c4.add("coco"); // 快速遍历 // for-each // Object 表示元素类型 // object 表示迭代变量(循环输出的变量) // c1表示集合 for (Object object : c4) { System.out.println(object.toString());//输出结果:apple(循环输出) } // banana // coco /**Iterator接口:几乎所有接口的都继承与它的超级接口 * 有hasNext方法:如果仍有元素可以迭代,则返回 true。 * next()方法:返回迭代的下一个元素。 * remove() : 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。 */ // 迭代器遍历(国内) /**快速遍历的本质 * Collection继承Iterable接口,表示集合支持快速遍历。Iterable接口定义了一个方法iterator()用于获取集合的迭代器, 是一个Iterator接口类型,iterator()内部返回一个实现类实现类Iterator接口。 这个实现类一定具有hasNext和next方法用于判断是否有下一个元素和获取下一个元素。快速遍历就是基于迭代器工作的。 */ //Iterator是对 collection 进行迭代的迭代器,蕴含了迭代的能力的接口 Iterator it = c4.iterator();////iterator是Iterable类的方法,通过方法的接口Iterator返回在此 collection 的元素上进行迭代的迭代器。 while(it.hasNext()) { //是否还有下个元素 Object item = it.next(); //Object是几乎所有类的父类,所以通过父类引用子类来实现方法 System.out.println(item.toString());//输出结果:apple(循环输出) } // banana // coco // 国外 for(Iterator it2=c4.iterator();it2.hasNext();) { Object item = it2.next(); System.out.println(item.toString()); } } }
package cn.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; /**1.3List接口(继承了collection接口) List 接口中的元素时有序的、可重复的。List接口中的元素通过索引(index)来确定元素的顺序。 有序的 collection(也称为序列)。可以对列表中每个元素的插入位置进行精确地控制。 用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素 */ public class ArraysList { public static void main(String[] args) { /**通过List接口实现类来实现,比collection接口本身使用实现类来实现更完善方法更多 * 增:add/addAll/add(index,el)/addAll(index,collection) * 删:clear/remove/removeAll/remove(index) * 改:set(index,el) * 查:get(index)/indexOf/lastIndexOf() * 其他:contains/containsAll/isEmpty/size */ List list1 = new ArrayList();//接口引用实现类,ArrayList类是collection接口和List接口共有的实现类 // 添加元素 list1.add("apple"); list1.add("banana"); // 在指定位置添加元素 list1.add(0, "coco"); System.out.println(list1);//输出结果:[coco, apple, banana] List list2 = new ArrayList(); list2.add("java"); list2.add("c++"); list1.addAll(1, list2); //把list2的元素放在list1的第一个位置 System.out.println(list1);//输出结果:[coco, java, c++, apple, banana] // 删除 //从此列表中移除第一次出现的指定元素(如果存在)(可选操作)。 list1.remove("coco"); System.out.println(list1);//输出结果:[java, c++, apple, banana] //移除列表中指定位置的元素(可选操作)。 list1.remove(0); System.out.println(list1);//输出结果:[c++, apple, banana] // 修改:指定元素替换列表中指定位置的元素(可选操作)。 list1.set(0, "javax"); System.out.println(list1);//输出结果:[javax, apple, banana] // 查: 返回列表中指定位置的元素。 System.out.println(list1.get(0));//输出结果:javax list1.add("apple"); list1.add("apple"); //List:元素是有序的、可重复的。 System.out.println(list1);//输出结果:[javax, apple, banana, apple, apple] //从左到右返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 System.out.println(list1.indexOf("apple"));//输出结果:1 //从右到左返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 System.out.println(list1.lastIndexOf("apple"));//输出结果:4 /**ListIterator 继承于Iterator,在Iterator的基础上提供了以正向遍历集合,也可以以逆序遍历集合。 * 所以有hasNext/next 以正向遍历 还多了 hasPrevious/previous 以逆序遍历 */ List list3 = new ArrayList(); list3.add("apple"); list3.add("banana"); list3.add("coco"); // 【1】快速遍历 for (Object item : list3) { System.out.println(item.toString());//输出结果:apple banana coco } // 【2】普通for(collection接口不行) //size()返回列表中的元素数。 for(int i=0;i<list3.size();i++) {//因为知道元素个数,所以通过循序遍历输出 System.out.println(list3.get(i));//输出结果:apple banana coco } // 【3】集合迭代器 Iterator it = list3.iterator(); while(it.hasNext()) { System.out.println(it.next());//输出结果:apple banana coco } //ListIterator接口是继承了Iterator接口,所以ListIterator接口能力更强大 // listIterator()返回此列表元素的列表迭代器(按适当顺序) //listIterator(int index) 返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。 // 正向遍历 ListIterator it2 = list3.listIterator(); while(it2.hasNext()) { System.out.println(it2.next());//输出结果:apple banana coco } // 逆序遍历 //hasPrevious()逆序输出 while(it2.hasPrevious()) { System.out.println(it2.previous());//输出结果: coco banana apple } ////listIterator(int index) 返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。 ListIterator it3 = list3.listIterator(1); while(it3.hasNext()) { System.out.println(it3.next());//输出结果:banana coco } } }
package cn.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; /**ArrayList 是List接口的实现类,底层数据结构是数组,实现大小可变的数组。 ArrayList 线程不安全,jdk1.2 ArrayList 底层数据结构是数组,默认数组大小是10,如果添加的元素个数超过默认容量, ArrayList会自动拓容,拓容原则:newCapacity = oldCapacity + oldCapacity / 2; 如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。 ArrayList作为List接口的实现类,常用方法和遍历方法参考List接口。 */ public class ArraysList01 { public static void main(String[] args) { ArrayList list1 = new ArrayList(); list1.add("apple1"); list1.add("apple2"); list1.add("apple3"); list1.add("apple4"); list1.add("apple5"); list1.add("apple6"); list1.add("apple7"); list1.add("apple8"); list1.add("apple9"); list1.add("apple10"); System.out.println(list1.size()); //结果输出:10 ArrayList list = new ArrayList(); list.add("apple1"); list.add("apple2"); list.add("apple3"); for (Object item: list) { System.out.println(item);//结果输出:apple1 apple2 apple3 } for (int i = 0; i < list.size(); i++) { Object item = list.get(i); System.out.println(item);//结果输出:apple1 apple2 apple3 } Iterator it = list.iterator(); while(it.hasNext()) { System.out.println(it.next());//结果输出:apple1 apple2 apple3 } ListIterator it2 = list.listIterator(); while(it2.hasNext()) { System.out.println(it2.next());}//结果输出:apple1 apple2 apple3 } }
package cn.collection; import java.util.ListIterator; import java.util.Vector; /*Vector 是List接口的实现类,底层数据结构也是数组,也是大小可变的数组。 Vector是线程安全的,jdk1.0 Vector底层数据结构是数组,默认数组大小是10,如果添加的元素个数超过默认容量,Vector会自动拓容, 拓容原则:newCapacity = oldCapacity +capacityIncrement(增长因子); 如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。 注意:Vector 在实现List接口的同时,同添加了自身特有的方法xxxElement, 未来使用时为了程序的可拓展性,一定要按照接口来操作Vector。*/ public class Vector01 { public static void main(String[] args) { Vector list = new Vector(); list.add("apple1"); list.add("apple2"); System.out.println(list.size());//输出结果:2 // 返回此向量的当前容量。 System.out.println(list.capacity());//输出结果:10 // 返回此向量中第一次出现的指定元素的索引,从 index 处正向搜索,如果未找到该元素,则返回 -1。 System.out.println(list.indexOf("apple2",1));//输出结果:1 //将指定的元素添加在容器末尾,容量加1 list.addElement("apple3"); ListIterator it = list.listIterator(); while(it.hasNext()) { System.out.println(it.next());}//结果输出:apple1 apple2 apple3 // vector:特有的方法都加了:Element } }
package cn.collection; import java.util.LinkedList; /*LinkedList是List接口的实现类,底层数据结构是链表。 LinekList常用方法和遍历方法参照List接口。 LinkedList 线程不安全。 除了实现List接口, 还实现栈接口 */ //push入栈操作 pop出栈操作 public class LinkedList01 { public static void main(String[] args) { //push入栈操作(add/remove/element() 可能会出现NoSuchElementException异常) LinkedList list = new LinkedList(); list.push("apple"); list.push("banana"); list.push("coco"); System.out.println(list.element());//获取表头元素:输出结果:coco(先入的在最底下) // pop出栈操作:从此列表所表示的堆栈处弹出一个元素。(先入后出)入口和出口相同 System.out.println(list.pop());//输出结果:coco System.out.println(list.pop());//输出结果:banana System.out.println(list.pop());//输出结果:apple //如果容器内已没有元素,再次 pop出栈操作就会 异常输出 //System.out.println(list.pop());// 输出结果:java.util.NoSuchElementException(没有元素异常) //以队列形式操作LinkedList,(先入后出)一个入口和一个出口(分开来的) LinkedList queue = new LinkedList(); // 入队 /** * 队列头 队列尾 *<----- <----- * [apple, banana, coco] */ queue.add("apple");//将指定元素添加到此列表的结尾。 queue.add("banana"); queue.add("coco"); System.out.println(queue);//输出结果:[apple, banana, coco] // 获取表头元素 System.out.println(queue.element());//输出结果:apple // 出队, System.out.println(queue.remove());//输出结果:apple System.out.println(queue.remove());//输出结果:banana System.out.println(queue.remove()); //输出结果:coco System.out.println(queue);//输出结果:[] //System.out.println(queue.remove());// java.util.NoSuchElementException // 入队(offer/poll/peek 可能会返回特殊值(null)) /**先入先出 * 队列头 队列尾 *<----- <----- * [apple, banana, coco] */ LinkedList list1 = new LinkedList(); list1.offer("apple"); list1.offer("banana"); list1.offer("coco"); System.out.println(list1);//输出结果:[apple, banana, coco] System.out.println(list1.peek());//输出结果:apple // 出队列 System.out.println(list1.poll());//输出结果:apple System.out.println(list1.poll());//输出结果:banana System.out.println(list1.poll());//输出结果:coco //获取但不移除此列表的头(第一个元素)。 System.out.println(list1.peek());//输出结果:null(因为已经全部取出了,容器已没有元素) LinkedList queue2 = new LinkedList(); // 入队 /**头和尾同时输出 *<----- <----- * [apple, banana, coco] * ----> -----> */ queue2.addFirst("apple"); queue2.addFirst("banana"); queue2.addFirst("coco"); System.out.println(queue2);//输出结果:[coco, banana, apple] System.out.println(queue2.getFirst());//表头:输出结果:coco //先输出尾在输出头最后才输出中间 System.out.println(queue2.removeLast());//输出结果:apple System.out.println(queue2.removeFirst());//输出结果:coco System.out.println(queue2.removeFirst());//输出结果:banana System.out.println(queue2);//输出结果:[] // 获取头元素 System.out.println(queue2.getFirst());//输出结果: java.util.NoSuchElementException } }
数据结构(补充)
数据结构就是数据在内存中存储结构。根据存储的方式不同,分为线性表、二叉树、图、栈、队列等
线性表
线性表数据按照一定的逻辑顺序存储在内存中。线性表是有序的。线性表根据内存的物理结构分为两种:数组和链表
数组是一种逻辑上有序的线性表,物理上也连续。
|
链表是一种逻辑上有序的线性表,但物理上不连续。
|
数组和链表的区别
数组在查询时效率高,在添加、删除元素时效率低(涉及移动元素)
链表在查询时效率低(每次从头开始,不能跳跃访问),在添加、删除元素时效率高(不涉及移动元素)
栈
特性:先进后出,后进先出
|
队列
特性:先进先出
![]()
|
package cn.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; /*Iterator在迭代过程中不允许向集合中添加元素 * 但ListIterator接口继承于Iterator,在这基础上拥有了在运行时增加元素的功能
当通过Iterator集合迭代器遍历集合过程中,不能再向集合汇总添加元素,否则出现ConcurrentModificationException 并发修改异常。
ListIterator允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置* */
public class Iterator1 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("apple"); list.add("banana"); list.add("coco"); /*Iterator it=list.iterator(); while(it.hasNext()) { String item = (String) it.next(); if(item.equals("banana")) { it.add("test");输出过程中增加元素(直接编译不了,属于检查性异常) } } System.out.println(list);*/ //ListIterator接口继承于Iterator,在这基础上拥有了在运行时增加元素的功能 ListIterator it1 = list.listIterator(); while(it1.hasNext()) { String item = (String) it1.next(); if(item.equals("banana")) { it1.add("test"); } } System.out.println(list);//输出结果:[apple, banana, test, coco] } }
package cn.collection; //泛型类:当一个类中属性的数据类型不确定时,具体是什么类型由使用者来确定时,使用泛型。泛型类的形式public class 类名<T> {} public class FanClass<T> { private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } public FanClass(T t) { super(); this.t = t; } public FanClass() { super(); } public void fan(){ System.out.println(t); } }
package cn.collection; import java.util.Arrays; /**/ public class Student { /*如果定义一个不知道什么类型的方法重载,要把每个类型写一遍,但用泛类型只用写一个就可以了 * 泛型方法可以定义多个泛型类型 多个泛型类型进一步优化了方法重载。 public void showInfo(int a) { System.out.println(a); } public void showInfo(float a) { System.out.println(a); } public void showInfo(String a) { System.out.println(a); }*/ /*泛型方法:当一个方法的参数类型不确定时,具体是什么类型由使用者来确定,可以考虑使用泛型方法。 形式:public <T> void xxx(T a) {System.out.println(a);} 泛型方法在调用时确定(指明)类型。 泛型方法在一定程度上优化了方法重载。*/ //用泛类型的方法重载 public <T> void showInfo(T a) { System.out.println(a); } // 可以定义多个泛型的类型 public <A,B> void showInfo(A a,B b) { System.out.println(a); System.out.println(b); } // 多个同类型的泛型 public <A> void print(A a) { System.out.println(a); } public <A> void print(A a,A b) { System.out.println(a); System.out.println(b); } /*多个同类型的泛型 A… a 表示方法可以接受多个参数。当调用方法传递多个参数时,多个参数被放到a数组中,a是什么类型的数组由开发者调用处传参决定。 print(A...a) 方法称为可变参数的泛型形式。*/ public void foo(int...a) {//多个同类型的int组成的数组a System.out.println(a); System.out.println(Arrays.toString(a)); } public <A> void print(A...a) {//多个同类型的泛型组成的数组a // a 是一个数组 // System.out.println(a); System.out.println(Arrays.toString(a)); } }
package cn.collection; //泛型(generic) /*泛型的概念: * 泛型允许开发者在强类型程序设计语言(java)编写代码时定义一些可变部分,这些部分在使用前必须作出指明。 泛型就是将类型参数化 ArrayList<E> list表示声明了一个列表list,列表的元素是E类型 ArrayList<String> list = new ArrayList<String>(); 声明了一个列表list,列表的元素只能是String类型。 泛型在编译器起作用,运行时jvm察觉不到泛型的存在。泛型在运行时已经被擦除了。 * */ public class Test { public static void main(String[] args) { //在提取时确定类型 FanClass<String> fan = new FanClass<String>(); fan.setT("apple"); fan.fan();//输出结果:apple FanClass<Integer> fan2 = new FanClass<Integer>(); fan2.setT(1); fan2.fan();//输出结果:1 Student stu = new Student(); stu.showInfo(1);//输出结果:1 stu.showInfo("apple",1.0f);//输出结果:apple 1.0 //stu.print(1, "apple");不行 //必须stu.print(1, 5);同类型 stu.print(1);//输出结果:1 stu.print(1,2);//输出结果:1 2 stu.print("apple");//输出结果:apple stu.print("apple","banana");//输出结果:apple banana stu.foo(1,2,3,4,5,6);//输出结果:[I@747541f8(证明它是一个数组) //输出结果:[apple, banana, banana, banana, banana] stu.print("apple","banana","banana","banana","banana");//输出结果:[apple, banana, banana, banana, banana] } }
ArrayList<String> list = new ArrayList<String>();//泛型的集合(因为集合底层结构是数组,所以基本类型要包装才能存进集合) list.add("apple"); ArrayList<Integer> list2 = new ArrayList<Integer>(); list2.add(1); // list instanceof ArrayList // list instanceof ArrayList<String> /*instanceof是Java、php的一个二元操作符(运算符),和==、>、<是同一类东西。由于它是由字母组成的,所以也是Java的保留关键字。 * 它的作用是判断其左边对象是否为其右边类的实例,返回boolean类型的数据。可以用来判断继承中的子类的实例是否为父类的实现。 * 相当于c#中的is操作符。java中的instanceof运算符是用来在运行时指出对象是否是特定类的一个实例。 * instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。*/ System.out.println(list instanceof ArrayList);//输出结果:true // System.out.println(list instanceof ArrayList<String>); System.out.println(list instanceof ArrayList<?>);//输出结果:true
package cn.collection; //如果接口中的方法的参数(形参、返回值)不确定时,可以考虑使用泛型接口。形式 public interface FANString<T> { public void showInfo(T t); } /** * 泛型的命名规则: * 【1】一定要大写,推荐使用单个字母 * 【2】字母任意,一般情况下下 T--type E---Element */
package cn.collection; //[1]实现类能确定泛型接口的类型 // public class Test00 implements FANString<String>{ @Override public void showInfo(String t) { // TODO Auto-generated method stub } } //实现类不能确定泛型接口的类型->继续泛。 /* public class Test00<T> implements FANString<T>{ @Override public void showInfo(T t) { } }*/
泛型的上限和下限 (C)
public static void print(ArrayList<? extends Pet> list) { for (Pet pet : list) { pet.showInfo(); } } |
泛型的上限ArrayList(? extends Pet) list 声明了一个容器,容器中的元素类型一定要继承于Pet,我们称这种形式叫做泛型的上限。
泛型的下限ArrayList(? super Pet) list 声明了一个容器,容器中的元素类型一定要是Pet的父类,我们称这个形式为泛型的下限。