概述
——- android培训、java培训、期待与您交流! ———-
一、集合框架
集合框架:又称集合类。可在API文档中查看java.util了解更多。
为什么会出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。数据存储用对象,对象存储用集合。
数组和集合类同为容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
集合的特点:
1.只用于存储对象的容器。
2.集合的长度是可变的。
3.集合可以存储不同类型的对象,不可以存储基本数据类型值。
集合容器因为内部的数据结构不同,有多种具体容器。不断的向上抽取,就形成了集合框架。
集合框架的构成及分类:
二、Collection接口
Collection接口常见的方法:
1.增加:boolean add(Object obj)//添加元素
boolean addAll(Collection coll)//将集合中的元素全部添加进该集合
2.删除:boolean remove(Object obj)//删除元素
void clear()//清空集合
3.判断:boolean contains(Object obj)判断是否包含obj元素
boolean containsAll(Collection coll)//判断是否包含该集合
boolean isEmpty()//判断集合中是否有元素。
4.获取:int size()//获取集合长度
Iterator iterator()//迭代器
5.转换:boolean retainAll(Collection coll)集合与集合取交集
Object toArray()//将集合转成数组Collection接口包含了两个子接口:
|---List:元素是有序的,元素可以重复,因为该集合体系有索引。
|---Set:元素是无序的,元素不可以重复。
List特有方法:凡是可以操作角标的方法都是该体系中的特有方法。
如:增:add(index,element)//在index角标位置上插入元素
addAll(index,collection)//在index角标位置上添加集合
删:remove(index)//移除角标index的元素
改:set(index,element)//修改角标index的元素
查:get(index)//根据角标index取元素
subList(from,to)//从角标from到to取元素
ListIterator()//List特有的迭代器
示例:
import java.util.*; class ListDemo { public static void main(String[] args) { List li=new ArrayList(); li.add("java01");//增加 li.add("java02"); li.add("java03"); li.add("java09"); sop("增",li); li.remove(1);//删除 sop("删",li); li.set(2,"java08");//修改 sop("改",li); System.out.println("查t"+li.get(2));//查看 List li1=li.subList(0,li.size()); sop("查",li1); } public static void sop(String str,List li) { Iterator it=li.iterator();//迭代器 System.out.print(str+"t"); while(it.hasNext()) { System.out.print(it.next()+" "); } System.out.println(); } }
运行结果为:
注:这里使用的Iteratord迭代器是对所有的Collection容器进行元素取出的公共接口。对象依赖于具体容器,而每一个容器的数据结构都不同,迭代器对象是在容器中进行内部实现的,也就是说iterator方法在每个容器中的实现方式是不同的。对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是iterator方法。
在List集合中有特有的迭代器——ListIterator,是Iterator的子接口。
在迭代时,不可以使用集合对象的方法操作集合中的元素。因为会发生异常。所以在迭代时只能用迭代器的方法操作元素,可是Iterator方法是有限的。只能对元素进行判断、取出、删除的操作。
如果想对元素进行其他操作(如:添加、修改等),就需要使用其子接口ListIterator。
示例:
import java.util.*; class ListDemo { public static void main(String[] args) { ArrayList al=new ArrayList(); al.add("java01");//增加 al.add("java02"); al.add("java03"); al.add("java09"); ListIterator li; for(li=al.listIterator();li.hasNext();)//往后查找元素 { if(li.next().equals("java03")) { li.set("java10");//修改 } } while(li.hasPrevious())//往前查找元素 { System.out.println(li.previous()); } } }
运行结果为:
List中常见的子类对象:
|---ArrayList:底层的数据结构使用的是数组结构。特点:查询速度快,但是删除稍慢,线程不同步。
|---LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。
|---Vector:底层使用的是数组数据结构。特点:线程同步,被ArrayList替代了。
示例:
import java.util.*; class ListDemo { public static void main(String[] args) { Vector v=new Vector(); v.add("java01");//增加 v.add("java02"); v.add("java03"); v.add("java09"); Enumeration en=v.elements(); while(en.hasMoreElements()) { System.out.println(en.nextElement()); } } }
运行结果为:
注:枚举(Enumeration)是Vector特有的取出方式。在这里可以发现枚举和迭代器很像,其实枚举和迭代器是一样的。因为枚举的名称过长,所以被迭代器所取代了。
LinkedList的特有方法:
1.增加:addFirst()
addLast()
2.获取:获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
getFirst()
getLast()
3.删除:获取元素,并删除元素。如果集合中没有元素,会出现NoSuchElementException
removaFirst()
removeLast()
在JDK1.6以后,出现了替代方法。
1.增加:和上述增加方法没什么不同
offFirst()
offLast()
2.获取:获取元素,但是不删除。如果集合中没有元素,会返回null。
peekFirst()
peekLast()
3.删除:获取元素,并删除元素。如果集合中没有元素,会返回null。
pollFirst()
pollLast()
示例:用LinkedList模拟堆栈(先进后出),队列(先进先出)
import java.util.*; class DuiLie { private LinkedList ls; DuiLie() { ls=new LinkedList(); } public void set(Object obj) { ls.addFirst(obj); } public Object get() { return ls.removeLast();//return ls.removeFirst(); } public boolean isNull() { return ls.size()>0; } } class LinkedList174 { public static void main(String[] args) { DuiLie dl=new DuiLie(); dl.set("java1"); dl.set("java2"); dl.set("java3"); dl.set("java4"); while(dl.isNull()) { System.out.println(dl.get()); } } }
运行结果为:
练习:自定义人对象作为元素存储到ArrayList集合中,并去除重复元素,如同姓名同年龄,视为同一个人。
import java.util.*; class Person//继承自Object { private String name; private int age; 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)//覆盖Object中的equals方法,equals在底层自动调用的 { System.out.println(this.name+"::::::"+this.age); if(!(obj instanceof Person)) return false; Person p=(Person)obj; return this.name.equals(p.name)&&this.age==p.age;//这里的equals是String的方法 } } class ArrayList175 { public static void main(String[] args) { ArrayList al=new ArrayList(); al.add(new Person("zhangsan",24)); al.add(new Person("lisi",49)); al.add(new Person("zhangsan",24)); al.add(new Person("wangwu",27)); al.add(new Person("zhangsan",24)); al.remove(new Person("wangwu",27));//remove的底层原理就是equals方法 ArrayList arr=delSame(al); Iterator it=arr.iterator(); while(it.hasNext()) { Person p=(Person)it.next(); System.out.println(p.getName()+";;;"+p.getAge()); } } public static ArrayList delSame(ArrayList al) { ArrayList arr=new ArrayList();//临时容器 Iterator li=al.iterator();//iterator()在collection接口中 while(li.hasNext()) { Object obj=li.next(); //System.out.println(obj); if(!arr.contains(obj))//contains的底层原理就是equals。当boolean contains(Object o); //o==null ? e==null : o.equals(e),当不为空时会自动调用equals arr.add(obj); } return arr; } }
运行结果为:
二、Set子接口
Set:元素是无序的(插入和取出的顺序不一定一致),元素不可以重复。
Set集合的功能和Collection是一致的。Set中常见的子类对象:
|----HashSet:底层数据结构是哈希表。线程不同步。
HashSet是如何保证元素的唯一性?
是通过元素的两个方法:hashCode和equals来完成的。如果元素的hashCode值相同,继续判断元素的equals方法是否为true。如果元素的hashCode值不相同,则不会调用equals方法。
示例:自定义人对象作为元素存储到HashSet集合中,并去除重复元素,如同姓名同年龄,视为同一个人。
import java.util.*; class Person { private String name; private int age; Person(String name,int age) { this.name=name; this.age=age; } public int hashCode()//复写父类Object的hashCode方法 { return name.hashCode()+age*12; } public boolean equals(Object obj)//复写父类Object的equals方法 { if(!(obj instanceof Person))//判断对象是否为Person类型 throw new RuntimeException("类型错误"); 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 HashSetDemo178 { public static void main(String[] args) { HashSet hs=new HashSet(); hs.add(new Person("zhangsan",24)); hs.add(new Person("lisi",49)); hs.add(new Person("zhangsan",24)); hs.add(new Person("wangwu",27)); hs.add(new Person("zhangsan",24)); for(Iterator it=hs.iterator();it.hasNext();) { Person p=(Person)it.next(); System.out.println("Name="+p.getName()+"tAge="+p.getAge()); } } }
运行结果为:
注意:对于判断元素是否存在,以及删除、添加等操作。依赖的方法是hashCode和equals方法。
|----TreeSet:可以对Set集合中的元素进行排序。默认按照字母的自然排序。底层数据结构是二叉树。保证元素唯一性的依据:compareTo方法return 0。
TreeSet排序的第一种方式:让元素自身具备比较性,元素需要实现Compareable接口,覆盖compareTo方法,这种方式也称为元素的自然顺序,或者叫做默认顺序。
示例:按年龄排序 Comparable比较对象与对象之间的关系
import java.util.*; class Person implements Comparable { private String name; private int age; Person(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)//复写 { //System.out.println(this.name+"::::::"+this.age); if(!(obj instanceof Person)) throw new RuntimeException("这不是Person对象"); Person p=(Person)obj; int num=new Integer(this.age).compareTo(new Integer(p.age)); if(num==0) return this.name.compareTo(p.name); return num; //this.name.equals(p.name)&&this.age==p.age;//这里的equals是String的方法 } } class TreeSet1505 { public static void main(String[] args) { TreeSet ts=new TreeSet();// ts.add(new Person("zhangsan",24)); ts.add(new Person("zisi",49)); ts.add(new Person("lisi",49)); ts.add(new Person("zhangsan",24)); ts.add(new Person("wangwu",27)); ts.add(new Person("zhangsan",24)); for(Iterator it=ts.iterator();it.hasNext();) { Person p =(Person)it.next(); System.out.println(p.getName()+"......"+p.getAge()); } } }
运行结果为:
TreeSet排序的第二种方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式——构造方法,即定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。比较器构造方式:定义一个类,实现Comparator接口,覆盖compare方法。
示例:按字符串长度排序
import java.util.*; class A implements Comparable { public int compareTo(Object obj) { return 0; } } class B implements Comparator { public int compare(Object obj1,Object obj2) { String s1=(String)obj1; String s2=(String)obj2; //int num= s1.length()-s2.length(); int num= new Integer(s1.length()).compareTo(new Integer(s2.length())); if(num==0)//长度相等时,次要条件 return s1.compareTo(s2); return num; } } class TreeSet1505 { public static void main(String[] args) { TreeSet ts=new TreeSet(new B());//比较器对象作为参数传递给TreeSet集合的构造函数。 ts.add("asjjjsddd"); ts.add("zasdd"); ts.add("vasdddd"); ts.add("sd"); ts.add("asd"); ts.add("dasdd"); for(Iterator it=ts.iterator();it.hasNext();) { Object o=it.next(); System.out.println(o); } } } <span style="font-size:14px;"> </span>
运行结果为:
注:当两种排序都存在时,以比较器为主。
三、泛型
在以上示例中发现运行结果中会报的安全问题。JDK1.5版本后出现了新特性——泛型,用于解决这类安全问题,是一个类型安全机制。
好处:
1.将运行时期出现的问题ClassCastException,转移到了编译时期。方便于程序员解决问题。让运行时期问题减少、安全。
2.避免了强制转换的麻烦。如在实现某一个接口时,指定传入接口方法的实参的类型的话,在复写该接口方法时就可以直接使用指定类型,而不需要强制转换。
格式:
通过< >来定义要操作的引用数据类型。
在使用Java提供的对象时,什么时候写泛型呢?
通常在集合框架中常见,只要见到<>就定义泛型,其实<>就是来接收类型的。当使用集合时,将集合中需要存储的数据类型作为参数传递到<>即可。注:只有引用类型才能作为泛型方法的实际参数。
泛型类:当类中要操作的引用数据类型不确定的时候,可定义泛型类。早期定义Object来完成扩展,现在定义泛型来完成扩展。
示例:自定义泛型类
class FanXing<QQ>//泛型类 { private QQ q; public void setObject(QQ q) { this.q=q; } public QQ getObject() { return q; } } class Worker { private String name; private int age; Worker(String name,int age) { this.name=name; this.age=age; } public String getName() { return name; } public int getAge() { return age; } } class FanXingDemo1507 { public static void main(String[] args) { FanXing<Worker> fx=new FanXing<Worker>(); fx.setObject(new Worker("zhangsan",12)); Worker wo=fx.getObject(); System.out.println("Name="+wo.getName()+"tAge="+wo.getAge()); } } <span style="font-size:14px;"> </span>
运行结果为:
泛型方法:泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类对象明确要操作的具体数据类型后,所有要操作的类型就已经固定了。即泛型类中明确好具体类型后,整个泛型类中的所以方法都是这个类型。
为了让不同方法可以操作不同类型,而且类型不确定,那么可将泛型定义在方法上。
如:class Demo
{
public <T> void show(T t) {}
public <E> void print(E t){}
}
注:静态方法不可以访问类上定义的泛型。如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
如:class Demo<T>//类型参数通常用单个大写字母表示
{
public static<T> void show(T t) {}
}
class Demo
{
public static<E,F> void print(){} //在泛型中可同时有多个类型参数,在定义它们的<>中用逗号分开
}
泛型定义在接口上
interface Inter<T> { void show(T t); } class InterImp<T> implements Inter<T> { public void show(T t) { System.out.println(t); } } class FanXingDemo1507 { public static void main(String[] args) { Inter<Integer> i=new InterImp<Integer>(); i.show(4); } }
运行结果为:
泛型限定:
1)?:通配符,也可以理解为占位符。
2)?extends E:可接收E类型或E类型的子类型;称之为上限。
如:ArrayList<? extends Number>x = new ArrayList<Integer>();
3)?super E:可接收E类型或E类型的父类型;称之为下限。
如:ArrayList<? super Integer>x = new ArrayList<Number>();
注:除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符。例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extends Serializable& cloneable> void method(){}。
四、Map集合
定义:该集合存储的是键值对,且要保证键的唯一性。
常见方法:
1.添加:
put(K key,V value):添加元素,如果出现添加时,相同的键,那么后添加的值会覆盖原有键对应值,并put方法会返回被覆盖的值。
putAll(Map <? extends K,? extends V> m):添加一个集合。
2.删除:
clear():清空
remove(Object key):删除指定键值对
3.判断:
containsValue(Object value):判断值是否存在
containsKey(Object key):判断键是否存在
isEmpty():判断是否为空
4.获取:
get(Object key):通过键获取对应的值,可通过返回值判断键是否存在,如null为不存在。
size():获取集合的长度
Collection<V> value():获取Map集合中所有的值,返回一个Collection集合。
Set<Map.Entry<K,V>> entrySet():将Map中的映射关系存入到Set集合中,而这个关系的数据类型就是Map.Entry
Set<K> keySet():将Map中的所有的键存入到Set集合中,因为Set具备迭代器,所以可以用迭代的方式取出所有的键,再根据get方法,获取每一个键对应的值。
Map常用的子类对象:
|---Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。JDK1.0,效率低。
|---HashMap:底层是哈希表数据结构。允许使用null键null值,该集合是不同步的。JDK1.2,效率高。
|---TreeMap:底层是二叉树数据结构。线程不同步。可以用于给Map集合中的键进行排序。
注:Set底层就是使用了Map集合。
Map集合取出元素的原理:将Map集合转成Set集合,再通过迭代器取出。
方式一:将Map中的映射关系存入到Set集合中,再通过迭代器取出。使用Set<Map.Entry<K,V>> entrySet()。
示例:
/*需求: 每个学生都有对应的归属地。学生student,地址String 学生属性:姓名,年龄 如姓名和年龄相同视为同一个学生,保证学生的唯一性 思路: 1描述学生 2定义map容器,将学生作为键,地址作为值,存入 3获取map集合中的元素 */ import java.util.*; class Student { private String name; private int age; Student(String name,int age) { this.name=name; this.age=age; } public String getName() { return name; } public int getAge() { return age; } public int hashCode() { //System.out.println("ff:"+(this.name.hashCode()+age*27)+"::"+(name.hashCode())); return this.name.hashCode()+age*27; } public boolean equals(Object obj) { if(!(obj instanceof Student)) //return false; throw new ClassCastException("类型错误"); Student s=(Student)obj; //System.out.println("dd:"+s.name); return this.name.equals(s.name) && (this.age==s.age); } } class MapTest1519 { public static void main(String[] args) { Map<Student,String> mp=new HashMap<Student,String>(); mp.put(new Student("zhangsan",39),"nanjing"); mp.put(new Student("lisi",26),"beijing"); mp.put(new Student("zhaoliu",30),"nanjing"); mp.put(new Student("zhangsan",39),"shanghai"); mp.put(new Student("wangwu",39),"shenzhen"); mp.put(new Student("lisi",26),"beijing"); mp.put(new Student("zhaoliu",30),"nanjing"); Set<Map.Entry<Student,String>> ss=mp.entrySet(); Iterator<Map.Entry<Student,String>> it=ss.iterator(); while(it.hasNext()) { Map.Entry<Student,String> me=it.next(); Student st=me.getKey(); String str=me.getValue(); System.out.println(st.getName()+"::"+st.getAge()+"::::"+str); } } }
运行结果为:
方式二:将Map中的所有的键存入到Set集合中,再通过迭代器取出。使用 Set<K> keySet()。
示例:如上示例中的Student类不变,只需改变entrySet的使用。如下:
class MapTest1519 { public static void main(String[] args) { Map<Student,String> mp=new HashMap<Student,String>(); mp.put(new Student("zhangsan",39),"nanjing"); mp.put(new Student("lisi",26),"beijing"); mp.put(new Student("zhaoliu",30),"nanjing"); mp.put(new Student("zhangsan",39),"shanghai"); mp.put(new Student("wangwu",39),"shenzhen"); mp.put(new Student("lisi",26),"beijing"); mp.put(new Student("zhaoliu",30),"nanjing"); Set<Student> s=mp.keySet(); Iterator<Student> it=s.iterator(); while(it.hasNext()) { Student key=it.next(); String value=mp.get(key); System.out.println(key.getName()+"::"+key.getAge()+"::::"+value); } } }
运行结果相同。
注:当量数据之间存在着映射关系的时候,就应该想到使用Map集合。
练习:需求:"sdfgzxcvasdfxcvdf"获取该字符串中字母出现的次数。结果为:a(1)c(2).....
/*需求:"sdfgzxcvasdfxcvdf"获取该字符串中字母出现的次数。 结果为:a(1)c(2)..... 思路:1.每一个字母对应一个数,有映射关系,可用map集合 2.将字符串转换成字符数组,因为要对每一个字母进行操作 3.定义一个map集合,因为打印结果的字母顺序,可用treemap集合 4.遍历字符数组,将每个字母作为键去查map集合 如果返回null,将该字母和1存入到map集合中 如果返回不为null,说明该字母已经在map中,并有对应次数 那么就获取该次数并进行自增,然后将该字符和自增后的次数存到map集合中,覆盖原有键对应的值 5.将map集合中的数据变成指定的字符串形式打印 */ import java.util.*; class TreeMap1521 { public static void main(String[] args) { //String str="sdfgzxcvasdfxcvdf"; String str="adfzxfxx"; String ss=charCount(str); System.out.println(ss); } public static String charCount(String str) { char[] ch=str.toCharArray(); Map<Character,Integer> tm=new TreeMap<Character,Integer>(); for(int i=0;i<ch.length;i++) { Integer value=tm.get(ch[i]);//通过键来查找,判断集合中是否包含该键的值 if(value==null) { tm.put(ch[i],1); } else { value+=1; tm.put(ch[i],value); } } //System.out.println(tm); StringBuilder sb=new StringBuilder();//用容器来装键值 Set<Character> s=tm.keySet(); Iterator<Character> it=s.iterator(); while(it.hasNext()) { Character c=it.next(); Integer in=tm.get(c); sb.append(c+"("+in+")"); //System.out.println(c+"::"+in); } //return null; return sb.toString(); } }
运行结果为:
扩展:以上所讲的是一对一的映射关系。但是在现实生活中有很多是一对多的映射关系,我们可以通过嵌套的形式将多个映射定义到一个大的集合中,再将大的集合分级处理,形成一个体系。
如:一个学校有很多班,一个班中有很多学生。可以定义为:
HashMap<String,HashMap<String,String>> czbk=new HashMap<String,HashMap<String,String>>();//学校
HashMap<String,String> yureban=new HashMap<String,String>();//班级
HashMap<String,String> jiuyeban=new HashMap<String,String>();//班级
czbk.put("yureban",yureban);
czbk.put("jiuyueban",jiuyeban);yureban.put("01","zhangsan");
jiuyeban.put("01","wangwu");
——- android培训、java培训、期待与您交流! ———-
最后
以上就是虚幻鱼为你收集整理的黑马程序员——Java基础:集合类、泛型的全部内容,希望文章能够帮你解决黑马程序员——Java基础:集合类、泛型所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复