毕向东之集合Collection与泛型
/* 为什么出现集合类: 面向对象语言对事物的体现都是对对象的形式,所以为了方便对多个对象的操作, 就对象进行存储,集合就是存储对象最常用的一种方式。 数组和集合类同是容器,有何不同? 数组虽然也可以存储对象,但长度是固定的,集合长度是可变的。 数组中可以存储基本数据类型,集合只能存储对象。 集合类的特点: 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。 2.集合中存储的都是对象的引用(地址)。 迭代器:就是集合的取出元素的方式。 */ import java.util.ArrayList; import java.util.Iterator; public class Test1 { public static void main(String[] args) { method_3(); } public static void method_3(){ ArrayList al=new ArrayList(); al.add("java01"); al.add("java02"); al.add("java03"); al.add("java04"); Iterator it=al.iterator();//获取迭代器,用于取出集合中的元素 while(it.hasNext()){//判断有没有内容 sop(it.next()); } } public static void method_2(){ ArrayList al1=new ArrayList(); al1.add("java01"); al1.add("java02"); al1.add("java03"); al1.add("java04"); ArrayList al2=new ArrayList(); al2.add("java01"); al2.add("java02"); al2.add("java05"); al2.add("java06"); al1.retainAll(al2);//取交集,al1中只会保留和al2中相同的元素 sop("al1:"+al1);//al1:[java01, java02] sop("al2:"+al2);//al2:[java01, java02, java05, java06] } public static void method_1(){ //创建一个集合容器。使用Collection接口的子类ArrayList ArrayList al=new ArrayList(); //1.添加元素 al.add("java01"); al.add("java02"); al.add("java03"); al.add("java04"); //打印集合 sop(al);//[java01, java02, java03, java04] //2.获取个数,集合长度 sop("size:"+al.size());//size:4 //3.删除元素 al.remove("java02");//[java01, java03, java04] al.clear();//清空集合 //4.判断元素 sop("java03是否存在:"+al.contains("java03")); sop("集合是否为空:"+al.isEmpty()); sop(al); } public static void sop(Object obj){ System.out.println(obj.toString()); } }
/* Collection(有的方法:add remove contains包含 clear清空 iterator) |--List:元素是有序的,元素可以重复,因为该集合体系有索引 注意:ArrayList和LinkedList中contains和remove判断的依据都是调用equals,所以需要重写 |--ArrayList:底层的数据结构使用的是数组结构。特点:查找速度快,增删慢。 线程不同步 |--LinkedList:底层的数据结构使用的是链表结构。特点:查找慢,增删快 |--Vector:底层是数组数据结构。 线程同步。 被ArrayList替代了 |--Set:元素是无序(存入和取出的顺序不一定一致)的,元素不可以重复。 |--HashSet:底层数据结构是哈希表(对于重复判断的依据是:哈希值与内容都相同时才表示重复) 如果元素的HashCode值相同才会去判断equals是否为true. 所以尽量保证hashCode值的唯一性,减少调用equals带来的效率 所以有时需要重写hashCode方法和equals方法 |--TreeSet:可以对Set集合中的元素进行排序(底层数据结构是二叉树:返回0,负数,正数) TreeSet排序的第一种方式:让元素自身具备比较性,元素需要 实现Comparable接口,覆盖compareTo方法 TreeSet排序的第二种方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的。 这时就需要让集合自身具备比较性。即让集合初始化时,就有了比较方式。 定义一个类,实现Comparator接口,覆盖compare方法。 当两种排序方式都存在时,以比较器为主。 List:(特有方法) 特有方法:凡是可以操作角标的方法都是该体系特有的方法。 增 add(index,element);//角标,元素 addAll(index,Collection);//添加集合 一堆元素 删 remove(index); 改 set(index,element); 查 get(index); subList(from,to);//从哪到哪 子串 listIterator(); List集合特有的迭代器:ListIterator是Iterator的子接口。 在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发送异常(不能并行两种方式同时操作集合) 所以,在迭代器时,只能使用迭代器的方法操作元素,可是Iterator方法是有限的, 只能对元素进行判断、取出、删除的操作 如果想要其他的操作(添加、修改等)就需要使用其子接口:ListIterator 该接口只能通过List集合的listIterator方法获取。 LinkedList:特有方法 addFirst(); addLast(); getFirst(); getLast();//获取元素,但不删除元素,如果集合中没有元素会出现NoSuchElementException removeFirst(); removeLast();//获取元素,但是元素被删除,如果集合中没有元素会出现NoSuchElementException 在jdk1.6出现了替代方法 offerFirst(); offerLast();//头部添加元素 peekFirst(); peekFirst();//获取元素,但是不删除元素,如果集合中没有元素会返回null pollFirst(); pollLast();//获取元素,但是元素被删除,如果集合中没有元素会返回null */ import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; public class Test2 { public static void main(String[] args) { ArrayList al=new ArrayList(); al.add("java01"); al.add("java02"); al.add("java03"); al.add("java04"); sop(al); ListIterator li=al.listIterator(); while(li.hasNext()){ Object ob=li.next(); if(ob.equals("java02")){ li.add("jia"); } } sop(al); } public static void sop(Object obj){ System.out.println(obj); } }
/*去除ArrayList集合中的重复元素 注意:ArrayList和LinkedList中contains和remove判断的依据都是调用equals,所以需要重写 */ import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; public class Test3 { public static void main(String[] args) { ArrayList al=new ArrayList(); al.add(new Person("张山",3));//相当于Object obj=new Person("张山",3); al.add(new Person("李四",4));//所以下面必须向下转型 al.add(new Person("王五",5)); al.add(new Person("王五",5)); al.add(new Person("张山",3)); al=singleElement(al); ListIterator it=al.listIterator(); while(it.hasNext()){ Person p=(Person)it.next(); sop(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)){//contains底层调用equals来判断是不是相等 newAl.add(obj); } } return newAl; } public static void sop(Object obj){ System.out.println(obj); } } 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; } public boolean equals(Object obj){//***参数列表不一致不叫复写 if(obj instanceof Person){ Person p=(Person) obj; if(this.getName()==p.getName() && this.getAge()==p.getAge()){ return true; } } return false; } }
/*需求:(元素具备比较性:都有年龄) 往TreeSet集合中存储自定义对象学生。 想按照学生的年龄进行排序 注意:排序时,当主要调节相同时,判断次要条件 只要控制compareTo方法返回的值就能控制元素的排序 因为String类中存在compareTo方法,按照字典的序列排序返回1 0 -1(表示大 相同 小) 所以当元素是字符串时,默认按照字典顺序排列 */ import java.util.Iterator; import java.util.TreeSet; public class Test4 { public static void main(String[] args) { TreeSet ts=new TreeSet();//默认Comparable,根据元素的自然顺序进行排序 ts.add(new Students("zhang",10)); ts.add(new Students("wang",11));//自动调用底层compareTo方法进行比较 ts.add(new Students("tang",11)); ts.add(new Students("huang",20)); Iterator it=ts.iterator(); while(it.hasNext()){ Students s=(Students)it.next(); sop(s.getName()+"..."+s.getAge()); } } public static void sop(Object obj){ System.out.println(obj); } } class Students implements Comparable{//该接口让Students类具备比较性 private String name; private int age; public Students(String name,int age){ this.name=name; this.age=age; } public String getName(){ return name; } public int getAge(){ return age; } public int compareTo(Object obj){ if(!(obj instanceof Students)) throw new RuntimeException("不是学生对象"); Students s=(Students)obj; if(this.age>s.age){//表明按照年龄的大小排序 return 1;//代表当前存的元素比前面已存的元素大,就存在已有元素的后面 }else if(this.age==s.age){//当年龄相同时,再判断姓名是否相同 return this.name.compareTo(s.name);//字符串.方法(); }//String类中的compareTo方法按照字典的序列排序返回1 0 -1(表示大 相同 小) return -1; } }
/* 元素自身不具备比较性,或者具备的比较性不是所需要的,这时需要让容器自身具备比较器。 定义比较器,将比较器对象作为参数传递给TerrSet集合的构造函数。 */ import java.util.*; public class Test5 { public static void main(String[] args) { TreeSet ts = new TreeSet(new MyCompare());//使用自己的排序方式 ts.add(new Students("zhang", 10)); ts.add(new Students("wang", 11));// 自动调用底层compareTo方法进行比较 ts.add(new Students("tang", 11)); ts.add(new Students("tang", 11)); ts.add(new Students("huang", 20)); Iterator it = ts.iterator(); while (it.hasNext()) { Students s = (Students) it.next(); sop(s.getName() + "..." + s.getAge()); } } public static void sop(Object obj) { System.out.println(obj); } } class MyCompare implements Comparator { public int compare(Object o1, Object o2) { Students s1 = (Students) o1; Students s2 = (Students) o2; int num = s1.getName().compareTo(s2.getName()); if (num == 0) {// 姓名相同判断年龄 return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge())); } return num; } }
import java.util.*; public class Test6 { public static void main(String[] args) { TreeSet ts=new TreeSet(new StrLenCompare()); ts.add("abs"); ts.add("abaa"); ts.add("absbf"); ts.add("aba"); Iterator it=ts.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } } class StrLenCompare implements Comparator{ public int compare(Object o1,Object o2){ String s1=(String)o1; String s2=(String)o2; // if(s1.length()>s2.length()) // return 1; // if(s1.length()==s2.length()) // return s1.compareTo(s2); // return -1; //=============第二种写法================================= int num=new Integer(s1.length()).compareTo(new Integer(s2.length())); if(num==0) return s1.compareTo(s2); return num; } }
/*泛型:jdk1.5版本以后出现的新特性,解决安全问题。<类>尖括号用来接收数据类型 因为集合添加的元素是Object类可以添加任何类型,但在用元素的方法时需要对元素进行向下转型 好处:1.将运行时期类型转换异常异常(ClassCastException),转移到了编译时期, 方便于程序员对问题的解决 2.避免了强制转换的麻烦 在比较器中写泛型,compare方法就不需要强制 */ import java.util.*; public class Test7 { public static void main(String[] args) { ArrayList<String> al=new ArrayList<String>();//String类型的容器 al.add("abc"); al.add("abcd"); al.add("ab"); //al.add(4);//运行时出错 Iterator<String> it=al.iterator();//String类型的迭代器给String的it, while(it.hasNext()){ //就不需要强制转换了 String s=it.next();//因为需要转为特定的类型,所以添加的元素必须一致 System.out.println(s.length());//才能使用长度的方法 } } }
/*泛型类:什么时候定义泛型类?注意:只能是引用数据类型(自定义类、Integer),不能是基本数据类型(int、char) 当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型类来完成扩展. */ public class Test8 { public static void main(String[] args) { Tool<Work> work=new Tool<Work>();//替代QQ类型 work.setQQ(new Work()); System.out.println(work.getQQ()); Tool1 t=new Tool1(); t.show("哈哈"); t.print(5); t.show(new Integer(5)); t.show(4); } } /*但是泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象 明确要操作的具体类型后,所有要操作的类型就已经固定了 为了让不同方法可以操作不同的类型,而且类型还不确定,那么就可以将泛型定义在方法上 特殊之处:静态方法不能访问类上定义的泛型,如果静态方法操作的引用数据类型不确定, 可以将泛型定义在方法上。 */ class Tool<QQ>{//QQ类型:泛型定义在类上 private QQ q; public void setQQ(QQ q){//局限性:随着类中QQ的确定而确定 this.q=q; } public QQ getQQ(){ return q; } public static <M> void method(M m){ System.out.println(m); }//因为静态比对象先加载,而类上的泛型需要在创建对象的时候才能确定,所以不能将M改为QQ } class Tool1{//泛型定义在方法上 public <T> void show(T t){//作用域只在该函数上 System.out.println(t); } public <T> void print(T t){//所以与上函数T没关系 System.out.println(t); }//只有定义了泛型,才能用泛型的名称,所以方法上的<T>不能不写 } class Work{} class Student{}
/*泛型既可以定义在方法上、类上、接口上、也可以同时定义在类上和方法上等等, 泛型只是解决了代码的复用性,使一个方法可以接收多种类型 泛型的高级应用(限定): ? 通配符,也可以理解为占位符(表示可以接收任意类型)。 ? extends E :可以接收E类型或者E的子类型。上限(限制了父类)。 ?super E :可以接收E类型或者E的父类型。下限(限制了子类)。 泛型中的继承关系 a、三种情况: (1)子类继承时,父类指定具体类型 子类可泛型也可以不泛型 格式:class 子类名<泛型字母表>extends 父类名<引用数据类型> (2)子类继承时,父类类型还是泛型 格式:class 子类名<泛型字母表a>extends 父类名<泛型字母表b> a包含b (3)类继承时,父类类型不指定类型 格式:class 子类名<泛型字母表>extends 父类名 子类可以泛型也可以不泛型 称之为泛型擦除,统一用Object代替 b、注意: (1)子类泛型>=父类泛型,也就是要比父类更宽的泛型 (2)属性: 父类属性类型随父类 子类属性类型随子类 (3)重写方法: 类型随父类 */ import java.util.*; public class Test9 { public static void main(String[] args){ ArrayList<Dog> al=new ArrayList<Dog>(); al.add(new Dog("dog1")); al.add(new Dog("dog2")); al.add(new Dog("dog3")); ArrayList<Animal> all=new ArrayList<Animal>(); all.add(new Animal("animal1")); all.add(new Animal("animal2")); all.add(new Animal("animal3")); print(al); print(all); } public static void print(ArrayList<? extends Animal> t){ Iterator<? extends Animal> it=t.iterator(); while(it.hasNext()){ System.out.println(it.next().getName()); } } } class Dog extends Animal{ public Dog(String name){ super(name); } } class Animal{ private String name; public Animal(String name){ this.name=name; } public String getName(){ return name; } }
import java.util.*; public class Test10 { public static void main(String[] args) { TreeSet<Studen> ts1=new TreeSet<Studen>(new ComPer()); ts1.add(new Studen("s1")); ts1.add(new Studen("s2")); ts1.add(new Studen("s3")); method(ts1); TreeSet<Worker> ts2=new TreeSet<Worker>(new ComPer()); ts2.add(new Worker("w1")); ts2.add(new Worker("w2")); ts2.add(new Worker("w3")); method(ts2); } public static void method(TreeSet<? extends Person1> t){//增加输出的复用性 Iterator<? extends Person1> it=t.iterator(); while(it.hasNext()){ System.out.println(it.next().getName()); } } } class ComPer implements Comparator<Person1>{//利用多态,子类都可以用这比较器 public int compare(Person1 p1,Person1 p2){//局限:但是只能用父类的方法 return p1.getName().compareTo(p2.getName()); } } class Studen extends Person1{ public Studen(String name){ super(name); } } class Worker extends Person1{ public Worker(String name){ super(name); } } class Person1{ private String name; public Person1(String name){ this.name=name; } public String getName(){ return name; } }
补充:
1.list集合的排序:
Collections.sort(list,new MyTimeSort()); //对list集合的排序 /** * 对时间的格式化和升序排序 想根据什么排就什么排,由compare的返回值决定 */ class MyTimeSort implements Comparator{ @Override public int compare(Object lhs, Object rhs) { String s1 = (String) lhs; String s2 = (String) rhs; long int1 = formatTime(s1); long int2 = formatTime(s2); return new Long(int1).compareTo(new Long(int2)); } }