黑马程序员——集合篇
------- android培训、java培训、期待与您交流! ----------
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:空间域名、网站空间、营销软件、网站建设、天山网站维护、网站推广。
1、集合类
为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一 种方式。
数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
2、集合框架的构成及分类
3、Collection接口:
常用方法:
1,添加:
add(object):添加一个元素
addAll(Collection) :添加一个集合中的所有元素。
2,删除:
clear():将集合中的元素全删除,清空集合。
remove(obj) :删除集合中指定的对象。注意:删除成功,集合的长度会改变。 removeAll(collection) :删除部分元素。部分元素和传入Collection一致。
3,判断:
boolean contains(obj) :集合中是否包含指定元素。
boolean containsAll(Collection) :集合中是否包含指定的多个元素。 boolean isEmpty():集合中是否有元素。
4,获取:
int size():集合中有几个元素。
5,取交集:
boolean retainAll(Collection) :对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。
6,获取集合中所有元素:
Iterator iterator():迭代器
7,将集合变成数组:
toArray();
4、List接口:元素是有序的,元素可以重复。因为该集合体系有索引。
常用子类:
ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
练习:
import java.util.*; import java.lang.*; /* 将自定义对象作为元素存到ArrayList集合中,并去除重复元素。 比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。 思路: 1,对人描述,将数据封装进人对象。 2,定义容器,将人存入。 3,取出。 List判断元素是否相同,依据的是元素的equals方法。 */ class Person { private String name; private int age; Person(String name,int age) { this.name = name; this.age = age; } public boolean equals(Object obj) { if (!(obj instanceof Person)) throw new RuntimeException("类型不匹配"); Person p = (Person)obj; System.out.println(this.name+"===="+p.name); return this.name.equals(p.name) && this.age == p.age; } public String getName() { return name; } public int getAge() { return age; } } class ArrayListTest2 { public static void sop(Object obj) { System.out.println(obj); } public static void main(String[] args) { ArrayList al = new ArrayList(); al.add(new Person("zhangsan",34)); al.add(new Person("zhangsan",34)); al.add(new Person("lisi",32)); al.add(new Person("wangwu",24)); al.add(new Person("wangwu",24)); al.add(new Person("zhaoliu",23)); al = singleElement(al); for (Iterator it = al.iterator();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))//调用底层方法调用的是equals方法。所以需要复写equals方法。 newAl.add(obj); } return newAl; } }
LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。不同步。
相关练习:
/* 使用LinkedList模拟一个堆栈或者队列数据结构。 堆栈:先进后出。如同一个杯子。 队列:先进先出。First in First out FIFO 如同一个水管。 */ import java.util.*; class DuiLie { private LinkedList link; DuiLie() { link = new LinkedList(); } public void myAdd(Object obj) { link.addFirst(obj); } public void myget() { return link.removeLast(); } public boolean isNull() { return link.isEmpty(); } }
Vector:底层是数组数据结构。线程同步。被ArrayList替代了。
相关练习:
import java.util.*; /* 枚举就是Vector特有的取出方式。 发现枚举和迭代器很像。 其实枚举和迭代时一样的。 为什么要设置枚举? 因为枚举的名称以及方法的名称都过长。 所以都被迭代器取代了。 枚举郁郁而终。 */ 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(); while (en.hasMoreElements()) { System.out.println(en.nextElement()); } } }
特有方法:
增:
add(index,element);在
addAll(index,Collection):
删:
remove(index);
改:
set(index,element);
查:
get(index);
subList(form,to);
listIterator();
List集合特有的迭代器。ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。所以,在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法时有限的。只能对元素进行判断,取出,删除的操作。如果要其他操作如添加,修改等,就需要使用其子接口,ListIterator。该接口只能通过List集合的ListIterator方法获取。
5、Set:元素是无序的,元素不可以重复。
|--HashSet:底层数据结构是哈希表。线程非同步的。
HashSet是如何保证元素的唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashCode值不同,不会调用equals。
注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
练习:
/* 往HashSet集合中存入自定义对象。 姓名和年龄视为同一人。重复元素。 */ import java.util.*; class HashSetTest { public static void sop(Object obj) { System.out.println(obj); } public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Person("a1",11)); hs.add(new Person("a1",11)); hs.add(new Person("a2",13)); hs.add(new Person("a3",15)); //hs.add(new Person("a4",17)); sop("a1"+hs.contains(new Person("a2",13))); sop("a1"+hs.remove(new Person("a3",15))); for (Iterator it = hs.iterator();it.hasNext() ; ) { Person p = (Person)it.next(); sop(p.getName()+","+p.getAge()); } } } class Person { private String name; private int age; Person(String name,int age) { this.name = name; this.age = age; } public int hashCode() { return name.hashCode()+age; } public boolean equals(Object obj) { if (!(obj instanceof 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; } }
|--TreeSet:可以对Set集合中的元素进行排序。
记住:排序时,当注意条件相同时,一定要判断一下次要条件。
因为底层数据结构是二叉树,保证元素唯一性的依据:compareTo方法return 0;
TreeSet排序的第一种方式:让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。
TreeSet的第二种排序方式。当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就具备比较性。
练习:
/* 练习:按照字符串长度排序。 字符串本身具备比较性,但是它的比较方式不是所需要的。 */ import java.util.*; class TreeSetTest { public static void main(String[] args) { TreeSet ts = new TreeSet(new StrLenCompare()); /*匿名内部类。 TreeSet ts = new TreeSet(new Comparator(){ public int compare(Object o1,Object o2) { if (!(o1 instanceof String && o2 instanceof String)) throw new RuntimeException("类型不匹配"); String s1 = (String)o1; String s2 = (String)o2; int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); if (num==0) return s1.compareTo(s2); return num; } }); */ ts.add("abcd"); ts.add("cc"); ts.add("cba"); ts.add("aaa"); ts.add("z"); ts.add("hahaha"); for (Iterator it=ts.iterator();it.hasNext() ; ) { System.out.println(it.next()); } } } class StrLenCompare implements Comparator { public int compare(Object o1,Object o2) { if (!(o1 instanceof String && o2 instanceof String)) throw new RuntimeException("类型不匹配"); String s1 = (String)o1; String s2 = (String)o2; int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); if (num==0) return s1.compareTo(s2); return num; } }
5、Map集合:
Map集合:该集合存储键值对。是一对一对往里村,要保证键的唯一性。
1,添加。
V put(K key, V value)
void putAll(Map extends K,? extends V> m)
2,删除。
void clear();
V remove(Object key)
3,判断。
boolean containsKey(Object key)
boolean containsValue(Object value)
boolean isEmpty()
4,获取。
V get(Object key)
int size()
Collection
5、 重点:
Set
Set
常用Map子类:
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合时线程同步的。JDK1.0,效率低
|--hashMap:底层是哈希表数据结构。允许使用null键null值,该集合是不同步的。JDK1.2 效率高。
|--TreeMap:底层是二叉树数据结构,线程不同步。可以用于给map集合中的键进行排序。
Map和Set很像。
其实Set底层就是使用了Map集合。
map集合的两种取出方式。
1,Set
2,Set
例子:
import java.util.*; class MapDemo2 { public static void main(String[] args) { Mapmap = new HashMap (); map.put("02","zhangsan02"); map.put("05","zhangsan05"); map.put("03","zhangsan03"); map.put("01","zhangsan01"); //将Map集合中的映射关系取出。存入到Set集合中。 Set > entrySet = map.entrySet(); for (Iterator > it = entrySet.iterator();it.hasNext() ; ) { Map.Entry me = it.next(); String key = me.getKey(); String value = me.getValue(); System.out.println("key:"+key+"=value:"+value); } /* //先获取map集合的所有键的set集合,keySet(); Set keySet = map.keySet(); //有了Set集合,就可以获取其迭代器。 Iterator it = keySet.iterator(); while (it.hasNext()) { String key = it.next(); //有了键就可以通过map集合的get方法获取其对应的值。 String value = map.get(key); System.out.println("key:"+key+"=value:"+value); } */ } } /* Map.Entry 其实Entry也是一个接口,它是Map接口中的一个内部接口。 */ interface Map { public static interface Entry { public abstract object getKey(); public abstract object getValue(); } }
6、常用工具类
Collections:它的出现给集合操作提供了更多的功能。这个类不需要创建对象,内部提供的都是静态方法。
静态方法:
Collections.sort(list);//list集合进行元素的自然顺序排序。
Collections.sort(list,new ComparatorByLen());///按指定的比较器方法排序。
class ComparatorByLen implements Comparator
public int compare(String s1,String s2){
int temp = s1.length()-s2.length();
return temp==0?s1.compareTo(s2):temp;
}
}
Collections.max(list); //返回list中字典顺序最大的元素。
int index = Collections.binarySearch(list,"zz");//二分查找,返回角标。
Collections.reverseOrder();//逆向反转排序。
Collections.shuffle(list);//随机对list中的元素进行位置的置换。
将非同步集合转成同步集合的方法:
Collections中的 XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
原理:定义一个类,将集合所有的方法加同一把锁后返回。
Collection 和 Collections的区别:
Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。
Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。
-------------------------------------------------------
Arrays:用于操作数组对象的工具类,里面都是静态方法。
asList方法:将数组转换成list集合。
String[] arr = {"abc","kk","qq"}
List
将数组转换成集合,有什么好处呢?用aslist方法,将数组变成集合;
可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set;
注意(局限性):数组是固定长度,不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear。(会报不支持操作异常UnsupportedOperationException);
如果数组中存储的引用数据类型,直接作为集合的元素可以直接用集合方法操作。
如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。
集合变数组:用的是Collection接口中的方法:toArray();
如果给toArray传递的指定类型的数据长度小于了集合的size,那么toArray方法,会自定再创建一个该类型的数据,长度为集合的size。
如果传递的指定的类型的数组的长度大于了集合的size,那么toArray方法,就不会创建新数组,直接使用该数组即可,并将集合中的元素存储到数组中,其他为存储元素的位置默认值null。
所以,在传递指定类型数组时,最好的方式就是指定的长度和size相等的数组。
将集合变成数组后有什么好处?限定了对集合中的元素进行增删操作,只要获取这些元素即可。
--------------------------------------------------------------------
System类:
描述系统一些信息。
获取系统属性信息:Properties getProgerties();
out:标准输出:默认是控制台。
in:标准输入,默认是键盘。
import java.util.*;
class SystemDemo
{
public static void main(String[] args)
{
Properties prop =System.getProperties();
//因为Properties是Hashtable子类,也就是Map集合的一个子类对象。
//那么可以通过map的方法取出该集合中的元素。
//该集合中存储的都是字符串,没有泛型定义。
//如何在系统中自定义一些特有信息呢?
System.setProperty("mykey","myvalue");
//获取指定属性信息。
String value = System.getProperty("os.name");
System.out.println("value="+value);
//可不可以在jvm启动时,动态加载一些属性信息呢?
//-D
String v =System.getProperty("hah");
System.out.println("v="+v);
/*
for(Object obj:prop.keySet())
{
String value =(String)prop.get(obj);
System.out.println(obj+"="+value);
}
*/
}
}
-----------------------------------------------------------------------
Runtim对象
该类并没有提供构造函数。
说明不可以new对象。那么会直接想到该类的方法都是静态的。
发现该类中还有非静态方法。
说明该类肯定会提供了方法获取本类对象,而且该方法是静态的。并且返回值类型是本类类型。
由这个特点可以看出该类使用了单例设计模式完成。
该方法是static Runtime getRuntime();
*/
class RuntimeDemo
{
public static void main(String[] args) throws Exception
{
Runtime r = Runtime.getRuntime();
Process p = r.exec("notepad.exe SystemDemo.java");//\为转义字符,所以应写为\\
//p.destroy();
}
}
------------------------------------------------------------------------
Date对象
可以对时间的进行操作的对象。
import java.util.*;
import java.text.*;
class DateDemo
{
public static void main(String[] args)
{
Date d = new Date();
System.out.println(d);
//将模式封装到SimpleDateFormat对象中。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 E kk:mm:ss");
//调用format方法,让模式格式化指定Date对象。
System.out.println(sdf.format(d));
}
}
-----------------------------------------------------
Calendar对象
import java.util.*;
import java.text.*;
class CalendarDemo
{
public static void main(String[] args)
{
Calendar c = Calendar.getInstance();
String[] month = {"一月","二月","三月","四月"
,"五月","六月","七月","八月"
,"九月","十月","十一月","十二月"};
String[] week ={"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
int index = c.get(Calendar.MONTH);
int index1= c.get(Calendar.DAY_OF_WEEK);
sop(c.get(Calendar.YEAR)+"年");
//sop(c.get((Calendar.MONTH)+1)+"月");
sop(month[index]);
sop(c.get(Calendar.DAY_OF_MONTH)+"日");
sop((c.get(Calendar.DAY_OF_WEEK)));
sop(week[index1]);
/*
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
sdf.format(d)
System.out.println(d);
*/
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
------------------------------------------------------------------------
泛型(Generic)
泛型:JDK1.5版本以后出现的新特性。用于解决安全问题。是一个安全机制。
好处:
1,将运行时期出现的问题ClassCastException,转移到了编译时期。
方便于程序员解决问题,让运行时期问题减少,安全。
2,避免了强制转换的麻烦。
泛型格式:通过<>来定义要操作的引用数据类型。
在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见。
只要见到<>就要定义泛型。
其实<>就是用了接受类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>即可。
class GenericDemo { public static void main(String[] args) { ArrayListal = new ArrayList (); al.add("abc01"); al.add("abc02"); al.add("abc0991"); al.add("abc014"); //al.add(4); for (Iterator it=al.iterator();it.hasNext() ; ) { String s = it.next(); System.out.println(s+":"+s.length()); } } }
泛型的使用
1、泛型类
在类声明时通过一个标识符表示类中某个字段的类型或者某个方法的返回值或参数的类型,这样在类声明或实例化的时候只要指定自己需要的类型就ok。
声明带泛型的类:
class 类名<泛型类型1,泛型类型2……>{
泛型类型 变量名;
泛型类型 方法名(){}
返回值类型 方法名(泛型类型 变量名){}
}
使用带泛型的类:
类名<具体类> 对象名 = new 类名<具体类>();
类型参数规范:推荐使用规范-常见的泛型,泛型只保存在源文件中,class文件中不存在;也就是说在编译阶段就会丢失,基本数据类型不能作为泛型类型;
K键,比如映射的键 key的类型
V值,比如Map的值 value类型
E元素,比如Set
T 泛型,Type的意思
泛型好处:限定添加类型和消除强转转换的麻烦!
练习例子
需求:设计一个表示点的类Point,该类有两个字段,一个是横坐标x,一个纵坐标y,要求坐标有3种表达形式(Integer,Double,String):
如果不使用泛型的话可以新建多个类,但是内部方法体只有参数类型不一样,所以用泛型的话更加简单,给定一个占位符,并不明确表示到底是什么类型,在实际运用的时候才确定类型!!
很好的例子!
package generic;
class Point
private T t1;
private T t2;
public T getT1() {
return t1;
}
public void setT1(T t1) {
this.t1 = t1;
}
public T getT2() {
return t2;
}
public void setT2(T t2) {
this.t2 = t2;
}
}
public class GenericDemo {
public static void main(String[] args) {
//String 类型的
Point
p.setT1("2");
p.setT2("3");
System.out.println(p.getT1());
System.out.println(p.getT2());
//Integer 类型的
Point
p2.setT1(23);
p2.setT2(24);
System.out.println(p2.getT1());
System.out.println(p2.getT2());
//Double 类型的
Point
p3.setT1(23.00);
p3.setT2(24.00);
System.out.println(p3.getT1());
System.out.println(p3.getT2());
-----------------------------------------------------------------------------------------------------------------------------------------------
Set
s.add("a");
//s.add(1);//此时就加不进去了,因为已经限制了容器内参数类型!
//此时就能保证集合里元素类型一致,
Set
//规定key只能是String,value是Date
Map
// V put(K key, V value)
Date v = map.put("", new Date());//和上面定义的类型一样
//V get(Object key)
Date val = map.get("");
}
}
运行结果
2
3
23
24
23.0
24.0
这样的话借助泛型一个类就可以表达多个不同类型的参数!
要求
消除强制类型的转换,如,使用Comparable比较时每次都需要类型强转;
1、没有加上泛型,最初的需要强制类型转换
package generic;
import java.util.Set;
import java.util.TreeSet;
class Person implements Comparable{//需要进行排序的类要实现Comparable
private Integer age;
public Person(Integer age) {
super();
this.age = age;
}
@Override
public int compareTo(Object o) {
Person p = (Person)o;//强制类型转换
return this.age.compareTo(p.age);
}
public String toString(){
return this.age.toString();
}
}
public class GenericDemo2 {
public static void main(String[] args) {
Set set = new TreeSet();
set.add(new Person(15));
set.add(new Person(12));
set.add(new Person(19));
set.add(new Person(53));
set.add(new Person(62));
System.out.println(set);
}
}
第二步:加上泛型,不再需要强转(因为类型已经固定了)!
package generic;
import java.util.Set;
import java.util.TreeSet;
class Person implements Comparable
private Integer age;
public Person(Integer age) {
super();
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.age.compareTo(o.age);//排序
}
public String toString(){
return this.age.toString();
}
}
public class GenericDemo2 {
public static void main(String[] args) {
Set
set.add(new Person(15));
set.add(new Person(12));
set.add(new Person(19));
set.add(new Person(53));
set.add(new Person(62));
System.out.println(set);
}
}
2、声明多个泛型类型和通配符
若一个类中多个字段需要不同的泛型声明,则在声明类的时候指定多个泛型类型即可;
格式:
public interface IDAO
PK add(T t);
void remove(PK id);
void update(PK id, T t);
T get(PK id);
}
在进行引用传递的时候泛型类型必须匹配才可以传递,否则编译不通过;
使用 ? ,表示未知类型的泛型对象:
List> 表示未知元素的List集合;
这种带通配符的List仅表示各种泛型List的父类,并不能把元素添加入集合中;
List> list = new ArrayList<>(); list.add(1);//ERROR
public void show(List> list){}
//表示可接受任意类型的List集合
3、泛型的上限与下限
设置泛型对象的上限使用extends,表示参数类型只能是该类型或该类型的子类:
声明对象:类名 extends 类> 对象名
定义类:类名<泛型标签 extends 类>{}
设置泛型对象的下限使用super,表示参数类型只能是该类型或该类型的父类:
声明对象:类名 super 类> 对象名称
定义类:类名<泛型标签 extends类>{}
public static void show(List extends Number> l){
}
public static void show(List super String> l){
}
public static void show(List extends Number> l){}
public static void show(List super String> l){}
泛型的上限
public static void main(String[] args) {
Person
p1.setVal(99);
Person
p2.setVal(3.14);
Person
p3.setVal("007");
show(p1);//√
show(p2);//√
show(p3);//×
}
public static void show(Person extends Number> p){//此处限定了Person的参数类型只能是Number或者是其子类,而String并不属于Number。
System.out.println(p.getVal());
}
泛型的下限
public static void main(String[] args) {
Person
p1.setVal(99);//Integer
Person
p2.setVal(3.14);//Double
Person
p3.setVal("007");//String
Person
p4.setVal(new Object());//Object
show(p1);//×
show(p2);//×
show(p3);//√
show(p4);//√
}
public static void show(Person super String> p){
System.out.println(p.getVal());
}
很好的例子!
package generic;
import java.util.ArrayList;
import java.util.List;
public class GenericDemo3 {
public static void main(String[] args) {
//因为show方法是用List>通配符接收的,所以可以是任意类型!
List
show(l1);
List
show(l2);
List
show(l3);
List
show(l4);
//使用up方法的话接收类型为Number或者其子类
//up(l1);//错误,因为up方法接收类型为Number或者其子类,l1(String)不符合!
up(l2);
up(l3);
//使用down方法的话接收类型为Number或者其父类
//down(l2);error
down(l3);
down(l4);
}
public static void down(List super Number> l){
for (Object object : l) {
System.out.println(object);
}
}
public static void up(List extends Number> l){
for (Object object : l) {
System.out.println(object);
}
}
public static void show(List> l){
for (Object object : l) {
System.out.println(object);
}
}
}
4、泛型接口和方法
java5后,可以声明泛型接口,声明方式和声明泛型类是一样的。
public interface IDAO
泛型接口子类有两种方式:
直接在子类后申明泛型;
当前题目:黑马程序员——集合篇
文章来源:http://pwwzsj.com/article/jpjjpj.html