面向对象语言对事物的体现都是以对象的形式,所以为了方便对多 个对象的操作,就对对象进行存储,集合就是存储对象常用的一 种方式。
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。 数组中可以存储基本数据类型,集合只能存储对象。
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象
Collection是集合框架中常用的接口,旗下有两个子接口:List(列表)和Set(集)
List:可存放重复元素,元素取出和存入的顺序是一致的
Set:不可以存放重复元素,元素的取出和存入的顺序不一定一致
一、Collection 接口中常用的方法
1、添加
1、all(E e) //E暂时可以看作是Object 可以添加任意类型
2、addAll(Collection collection) 将指定集合中的元素添加到该集合中
2、删除
1、remove(E e) //删除指定元素
2、remove(Collection collection) //去交集 ,也就是该集合中会保留指定集合中没有的元素,如果指定集合中的元素与该集合中的元素完全相同,那么该集合将为NULL
3、clear() //删除集合中的所有元素
3、判断
1、contains(E e) //判断指定元素是否存在
2、isEmpty(); //判断集合是否为NUll 其实就是在判断size()是否为0
4、获取
1、size() //获取集合的长度
2、iterator() 迭代器,用于获取集合中的每个元素 和for循环原理相同
二、迭代器
1、什么是迭代器?
迭代器其实就是集合中取出元素的方式 如:DNF中的娃娃机
2、对于迭代器取出元素的动作:
当不足以用一个函数来描述,需要多个功能来体现时,所以就将取出这个动作封装成了一个对象,用对象来进行描述,所以就把取出方式定义在集合内部,这样取出方式就可以直接访问集合中的元素,那么取出方式就被定义成了内部类。
因为每个容器的数据结构不同,所以取出的动作和细节也不一样,但是都有共性的内容:判断和取出,那么可以将这些共性进行抽取,那么该类就就都符合一个规则,该规则就是Iterator接口,而想要获取集合中取出的对象,可以通过一个对外提供的方法iterator()来获取
3、迭代常用方法
1、next() 取出一个元素
2、hasNext() 判断是否还有元素 返回一个boolean类型的结果
3、remove() 删除当前元素
注意:想要将集合中的元素全部取出,必须使用循环每取一次(next()),判断一次(hasNext()) 知道hasNext为false,结束循环
4、迭代用法:
注意:iterator是Collection接口中的方法,所以每一个子类集合对象多具备迭代器ArrayList al1 = new ArrayList(); al1.add("java01"); al1.add("java02"); al1.add("java03"); al1.add("java04"); //第一种用法 Iterator it = all.iterator(); while(it.hasNext()){ System.out.println(it.next()); } //第二种用法 for(Iterator it = all.iterator();it.hasNext();){ System.out.prinln(it.next()); }
5、使用迭代注意事项
1、 迭代器在Collection接口中是通用的,它替代了Vector类中中的枚举
2、迭代器的next方法是自动向下取元素,但是一个循环中不可以出现多个next,要避免出现NoSuchElementException异常
3、迭代器的next方法返回值类型是Object,所以应注意转换
问题:为什么迭代器的next方法的返回值类型是Object呢?
原因:因为集合中可以存储的类型只能是对象(String、int都是对象),所以返回值一定是对象的祖先Object
List
Collection
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。 底层使用的都是可变数组,每次加长50%
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。 底层使用的都是可变数组,每次加长100%
1、elements() //相当于iterator()
2、hasMoreElements() 相当于hasNext(0
3、nextElement() //相当于next()
import java.util.*; class VectorDemo { public static void main(String[] args) { Vector v = new Vector(); v.add("java01"); v.add("java02"); v.add("java03"); v.add("java04"); Enumeration en = v.elements(); //elements返回值类型为Enumeration类 while(en.hasMoreElements()) { System.out.println(en.nextElement()); } } }
一、List中特有的方法
只要是可以操作角标的方法都是List集合中特有的方法,因为List集合有索引
1、增
add(int index)
add(int index, Collection collection)
2、删
remove(int index)
3、改
set(int index)
4、查
get(index)
subList(头, 尾)
indexOf(int index)
lastIndexOf(int index)
listIterator() //注:有点特殊
二、listIterator方法
1、用法:listIterator方法的用法和iterator方法的用法相同,只不过listIterator的返回值类型为ListIterator,是 Iterator的一个子接口,而 iterator方法返回值类型为Iterator接口
2、listIterator和iterator的区别:
因为iterator方法返回的结果是Iterator ,而Iterator接口中仅仅只有对元素的删除操作,所以当使用Iterator是不可以对元素进行出删除操作之外的操作的,有局限性,而ListIterator类中定义了很多对元素操作的方法,所以当需要多元素进行删除以外的操作时,建议使用Iterator的子接口ListIterator中的listIterator方法获取
3、ListIterator特有方法
1、get()
2、add()
3、hasPrevious() 判断前面是否还有元素
4、previous() 向前获取元素
5、set(Object obj);
LinkedList
一、LinkedList特有方法
1、增
addFirst();
addLast();
2、删 注:使用该方法时,该方法不仅会删除元素,还会返回要删除的元素 如果集合为空 则会出现NoSuchElementException
removeFrist()
removeLast()
3、获取 注:使用该方法时,该方法只会获取元素,不会删除元素 如果集合为空 则会出现NoSuchElementException
getFirst();
getLst()
在JDK1.6以后,出现了替代方法。
1、增
offFirst();
offLast();
2、获取
//获取元素,但是不删除。如果集合中没有元素,会返回null。
peekFirst();
peekLast();
3、删
//获取元素,并删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
4、练习1:
5、练习2:去除Arraylist中的重复元素/* 需求:模拟一个堆栈和队列数据结构 堆栈:先进后出 如同一个杯子 队列:先进先出 如同一个管子 */ class DemoTest { private LinkedList link; //初始化一个LinkedList集合 DemoTest(){ this.link = new LinkedList(); } //自己内部完成添加 public void myAdd(Object obj){ link.addFrist(obj); } //堆栈 public void getDuiZhan(){ while(!link.isEmpty()){ System.out.println(link.removeFirst()); } } //队列 public void getDuiLie(){ while(!link.isEmpty()){ System.out.println(link.removeLast()); } } } public class Test { public static void main(String [] agrs){ DemoTest de = new DemoTest(); de.myAdd("lisi01"); de.myAdd("lisi02"); de.myAdd("lisi03"); de.myAdd("lisi04"); de.myAdd("lisi05"); //输出队列 de.getDuiLie(); //输出堆栈 de.getDuiZhan(); } }
/* 需求:去除List集合中的重复元素 */ class ListUtil { //消除重复值后输出 public static void get(List li){ for(Iterator it = contains(li).iterator();it.hasNext();){ System.out.println(it.next()); } } //把重复元素清除后添加到一个新的List集合中 private static List contains(List li){ List list = new ArrayList(); for(Iterator it = li.iterator();it.hasNext();){ Object obj = it.next(); if(!list.contains(obj)) list.add(obj); } return list; } } public class DemoTest { public static void main(String [] args){ LinkedList list = new LinkedList(); list.add("li001"); list.add("li001"); list.add("li003"); list.add("li002"); list.add("li005"); list.add("li002"); ListUtil.get(list); } }
6、练习3、去除集合中的重复对象
import java.util.*; class Person { private String name; private int age; Person(String name,int age) { this.name = name; this.age = age; } /*这里必须复写equals方法*/ public boolean equals(Object obj) { //用于判断equals方法是否执行 System.out.println("??????????"); if(!(obj instanceof Person)) return false; Person p = (Person)obj; //这里只要是名字和年龄相同就认为是同一个人 return this.name.equals(p.name) && this.age == p.age; } public String getName() { return name; } public int getAge() { return age; } } class DemoTest3 { public static void sop(Object obj) { System.out.println(obj); } public static void main(String[] args) { ArrayList set = new ArrayList(); //Set set = new HashSet(); set.add(new Person("lisi01",30));//al.add(Object obj);//Object obj = new Person("lisi01",30); set.add(new Person("lisi02",32)); set.add(new Person("lisi02",32)); set.add(new Person("lisi04",35)); set.add(new Person("lisi03",33)); set.add(new Person("lisi04",35)); /*发现在Person类中复写了equals方法之后,并没有去除重复对象,这是为什么呢? 我们都知道对象比较的是equals方法,为什么我们复写了equals方法还是不行呢? 因为如果我们在equals方法中打印输出一条语句的话,我们就会发现该语句根本就没有执行? 这又是为什么呢?这就说明计算机底层并没有帮我们调用我们复写的equals方法 而不执行equals方法肯定是去除不掉重复对象的,所以这时候我们最大的问题应该是调用equals 方法,只要执行了equal方法那么肯定能去除重复对象,所以,这时候我们contain方法来解决 因为contains方法底层用的都是equals方法,我们调用了contains不就等于调用了equals方法么? 所以,我们可以先用contain判断一下,把不存在的值放入到一个新容器中,所以就有了singleElement方法*/ set = singleElement(set); for(Iterator it = set.iterator();it.hasNext();){ Person p = (Person) it.next(); System.out.println(p.getName()+"...."+p.getAge()); } } public static ArrayList singleElement(ArrayList al) { //定义一个临时容器。 ArrayList newAl = new ArrayList(); Iterator it = al.iterator(); while(it.hasNext()) { Object obj = it.next(); if(!newAl.contains(obj)) newAl.add(obj); } return newAl; } }
Set
Set:无序,不可以重复元素。
|--HashSet:数据结构是哈希表。线程是非同步的。
保证元素唯一性的原理:判断元素的hashCode值是否相同。
如果相同,还会继续判断元素的equals方法,是否为true。
|--TreeSet:可以对Set集合中的元素进行排序。
底层数据结构是二叉树。
保证元素唯一性的依据:
compareTo方法return 0.
TreeSet排序的第一种方式:让元素自身具备比较性。
元素需要实现Comparable接口,覆盖compareTo方法。
也种方式也成为元素的自然顺序,或者叫做默认顺序。
TreeSet的第二种排序方式。
当元素自身不具备比较性时,或者具备的比较性不是所需要的。
这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。注:Set接口中的方法和Collection接口中的方法是一样的,所以在使用Set子类时获取元素时,只能通过迭代的方式
HashSet
注:HashSet对于判断元素是否存在,以及删除等操作,依赖的方法都是元素的hashCode方法和equals方法
练习:去除HashSet中的重复对象
/*需求:消除HashSet中的重复对象*/ import java.util.*; class Person { private String name; private int age; public Person(String name,int age){ this.name = name; this.age = age; } public String getName(){ return name; } public int getAge(){ return age; } //重写Object类中的hashCode方法 public int hashCode(){ //这样写能尽可能的保证哈希值唯一 return this.name.hashCode()+age*24; } //重写Object类中的equals方法 public boolean equals(Object obj){ if(!(obj instanceof Person)) throw new RuntimeException(); Person p = (Person) obj; return this.name.equals(p.name); } } public class HashSetDemo { public static void main(String[] args) { Set set = new HashSet(); set.add(new Person("li01",21)); set.add(new Person("li01",21)); set.add(new Person("li02",21)); set.add(new Person("li03",21)); set.add(new Person("li05",21)); set.add(new Person("li02",21)); for(Iterator it = set.iterator(); it.hasNext();){ Person p = (Person) it.next(); System.out.println(p.getName()+":"+p.getAge()); } } }
TreeSet
特点:
可对Set集合中的元素进行排序,是因为:TreeSet类实现了Comparable接口,该接口强制让增加到集合中的对象进行了比较,需要复写compareTo方法,才能让对象按指定需求(如人的年龄大小比较等)进行排序,并加入集合。
java中的很多类都具备比较性,其实就是实现了Comparable接口。
二叉树原理:通过compareTo方法的返回值,是正整数、负整数或零,则两个对象较大、较小或相同。根据返回值来存放,正数则放在右边,负数这放在左边,0则证明相等,不存入
2、Tree排序的两种方式 当自然排序和比较器同时存在时,以比较器为主
1)第一种排序方式:自然排序
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也被称为元素的自然顺序,或者叫做默认顺序。
示例:
/* TreeSet自然排序 */ import java.util.*; class Person implements Comparable { private String name; private int age; public Person(String name,int age){ this.name = name; this.age = age; } public String getName(){ return name; } public int getAge(){ return age; } //重写comepareTo()方法让对象具备比较性 public int compareTo(Object obj){ if(!(obj instanceof Person)) throw new RuntimeException(); Person p = (Person) obj; //让对象先按姓名排序后如果姓名相同再按年龄排序 int num = this.name.compareTo(p.name); if(num==0) return this.age - p.age; // return new Integer(this.age).compareTo(new Integer(p.age)) return num; } } public class TreeSetDemo { public static void main(String[] args) { Set set1 = new TreeSet(); set1.add(new Person("li01dfds————",23)); set1.add(new Person("li01dsfs————",21)); set1.add(new Person("li02assa————",24)); set1.add(new Person("li03fdds————",20)); set1.add(new Person("li05asf————",19)); set1.add(new Person("li02sdfs————",21)); show(set1); } public static void show(Set set){ for(Iterator it = set.iterator(); it.hasNext();){ Person p = (Person) it.next(); System.out.println(p.getName()+":"+p.getAge()); } } }
2)第二种方式:比较器
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
比较器构造方式:定义一个类,实现Comparator接口,覆盖compare方法。创建一个带有Comparator对象的构造函数
当两种排序都存在时,以比较器为主。
示例:
/*比较器*/ public class TreeSetDemo { public static void main(String[] args) { Set set1 = new TreeSet(new Com()); set1.add(new Person("li04————",23)); set1.add(new Person("li02————",21)); set1.add(new Person("li00————",24)); set1.add(new Person("li03————",20)); set1.add(new Person("li05————",19)); set1.add(new Person("li01————",21)); show(set1); } public static void show(Set set){ for(Iterator it = set.iterator(); it.hasNext();){ Person p = (Person) it.next(); System.out.println(p.getName()+":"+p.getAge()); } } } //比较器排序 class Com implements Comparator { public int compare(Object obj1,Object obj2){ Person p =(Person)obj1; Person p2 =(Person)obj2; int num = p.getName().compareTo(p2.getName()); if (num==0) return new Integer(p.getAge()).compareTo(new Integer(p2.getAge())); return num; } }
泛型
泛型是JDK1.5版本后出现的新特性,用来解决安全问题,是一个安全机制
1、把运行时期会遇到的类型转换错误(ClassCastException)转移到编译时期,方便程序员解决问题
2、避免了强制转换的麻烦
1、泛型定义在类上(泛型类)
特点:泛型类定义在类上时,泛型在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体泛型后,所有操作的类型就已经固定了
2、泛型定义在方法上/*泛型定义在类上*/ class FanXing
{ private T t; public void setObject(T t){ this.t = t; } public T getObject(){ return t; } public void show(T t){ System.out.println(t); } } class Student { } class Work { } class FanXingDemo { public static void main(String[] args) { /* 会出错的情况: 1、 FanXing f = new FanXing (); f.setObject(new Student()); //这样写会报错 因为前面返回值类型已经固定为Student Work w = f.getObject(); 2、 FanXing f = new FanXing (); //这样写会报错 因为类型已经固定为Work f.setObject(new Student()); Work w = f.getObject(); 3、 FanXing f = new FunXing (); f.show("mmmmmmmm"); //这样写会报错,因为类型已经固定位String类型 f.show(4); */ /* 不会出错的情况 1、 FanXing f = new FanXing (); f.setObject(new Student()); Student s = f.getObject(); 2、 FanXing f = new FunXing (); f.show("mmmmmmmm"); f.show("kkkkkkkk"); */ } }
3、泛型定义在接口上class FanXing1
{ public void show(T t){ System.out.println(t); } } class FanXingDemo { public static void main(String[] args) { /*泛型定于在方法上,就没有了局限性 虽然说类上也定义了泛型,但是方法上使用的并不是类上泛型的类型,所以不会报错 */ FanXing1 f1 = new FanXing1 (); f1.show("aaaaaaaaaaaaa"); f1.show(5); f1.show(new Integer(111111)); f1.show('b'); } }
泛型高级应用:/* 泛型定义在接口上 */ interface Demo
{ void show(T t); } class DemoTest implements Demo //当不确定使用什么类型时,可以不定义类型,让子类去定义 { public void show(T t){ //泛型类型必须和接口的泛型一致,不然不会被重写,DemoTest类也会变成抽象类 System.out.println(t); } } public class FanXingInterface { public static void main(String[] args) { /* 会出错的情况: 1、 //泛型类型不一致 DemoTest d = new DemoTest (); d.show("aaaaaaaaa"); 2、 //泛型类型不一致 DemoTest d = new DemoTest (); d.show(3); */ //不会出错 DemoTest d = new DemoTest (); d.show(3); } } 1、?通配符 相当于Object
2、? extends E 可以接收E类型或E类型的子类型
3、? super E 可以接收E类型或E的父类型
import java.util.*; /* ? 通配符。也可以理解为占位符。 泛型的限定; ? extends E: 可以接收E类型或者E的子类型。上限。 ? super E: 可以接收E类型或者E的父类型。下限 */ class Person { private String name; Person(String name) { this.name = name; } public String getName() { return name; } } class Student extends Person { Student(String name) { super(name); } } class GenericDemo6 { public static void main(String[] args) { ArrayList
al = new ArrayList (); al.add(new Person("abc1")); al.add(new Person("abc2")); al.add(new Person("abc3")); printColl(al); ArrayList al1 = new ArrayList (); al1.add(new Student("abc--1")); al1.add(new Student("abc--2")); al1.add(new Student("abc--3")); printColl(al1); //ArrayList extends Person> al = new ArrayList ();error } public static void printColl(Collection extends Person> al) //这样写代表printColl函数可以接收Collection集合,而collection集合中的元素必须是Person类型或Person的子类型 { Iterator extends Person> it = al.iterator(); while(it.hasNext()) { System.out.println(it.next().getName()); } } /* public static void printColl(ArrayList> al) //代表printColl函数可以接收的类型是ArrayList集合,而集合中的元素可以是任意类型 { Iterator> it = al.iterator(); while(it.hasNext()) { System.out.println(it.next().toString()); } } */ }