一集合类概述
1.为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,对象都是确定性的可以区分的,所以可以存储起来,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用 的一种方式。
2.数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
注意:
集合中为什么不能存放基本数据类型?
集合中存放的可都是对象的引用,实际内容都在堆上面或者方法区里面,但是基本数据类型是在栈上分配空间的。随时就被收回的,是不确定的,集合只存储确定的元素。但是通过自动包装类就可以把基本类型转为对象类型,存放引用就解决了这个问题。
首先,集合的存在就是为了方便对多个对象的操作才出现的,集合是存储对象最常用的一种方式,也就是说,集合的从有到无都是因为对象,人们发现要保存多个对象很麻烦,于是便发明了集合,集合是依赖对象而生的,所以就对基本数据类型"不感兴趣",但是,现在基本数据类型都有了其对应的封装的对象,而且有了自动拆箱和装箱功能,基本数据类型和其对应对象之间的转换变得很方便,想把基本数据类型存入集合中,直接存就可以了,系统会自动将其装箱成封装类
然后加入集合当中
例如:下面的代码中,我就直接把int类型加入了集合中,java会自动将int类型包装成Integer对象,再装入集合中
List<Integer> list = new ArrayList<Integer>();
list.add(1);
//打印结果为1
2.集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
集合框架图:
注意:为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
二、集合框架共性方法
1.集合类的由来:
对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。
就使用集合容器进行存储。
集合特点:
(1).用于存储对象的容器。
(2).集合的长度是可变的。
(3).集合中不可以存储基本数据类型值。
集合容器因为内部的数据结构不同,有多种具体容器。
不断的向上抽取,就形成了集合框架。
Collection
|--List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
|--Set:元素不能重复,无序。
框架的顶层Collection接口:
Collection的常见方法:
(1)添加。
boolean add(Object obj):
boolean addAll(Collection coll):
(2)删除。
boolean remove(object obj):
boolean removeAll(Collection coll);
void clear();
(3)判断:
boolean contains(object obj):
boolean containsAll(Colllection coll);
boolean isEmpty():判断集合中是否有元素。
(4)获取:
int size():
Iterator iterator():取出元素的方式:迭代器。
该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。
所以该迭代器对象是在容器中进行内部实现的。
对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,
也就是iterator方法。
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
其实就是抓娃娃游戏机中的夹子!
(5)其他:
boolean retainAll(Collection coll);取交集。
Object[] toArray():将集合转成数组。
注意:add方法的参数类型是object,以便于接受任意类型对象
集合中存储的都是对象的引用地址:举例:当在集合中存入一个Person类对象,实际存入集合的是对象的引用地址。如图:
三、迭代器的理解
1.什么是迭代器?
其实就是集合的取出元素的方式
(1)当每个容器都有存和取得动作,但是每个容器存取的动作又不一样时,当不足以用一个函数来描述,需要用多个功能来体现,所以就将取出这个动作封装成一个对象来描述。就把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素。那么取出方式就被定义成了内部类。
(2)而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都具有共性内容:判断和取出。那么就可以将这些共性抽取。
(3)那么这些内部类都符合一个规则(或者说都抽取出来一个规则)。该规则就是Iterator。通过一个对外提供的方法:iterator();,来获取集合的取出对象。
因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
利用迭代器获取容器对象格式:
// 迭代器对象是通过容器的内部类方法获取出来的,返回的是Iterator接口的子类对象。利用了多态机制。
Iterator it = al.iterator();
//利用迭代器的方法判断时候有下一个元素
while(it.hasNext())
{
//把对象取出,对象都是object类型的子类;
Object obj=it.next();
}
注意:itnext()取数据时,是有游标的,第一次指的是,
三、List集合
1.List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
|--LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。
2.List特有方法:
这些方法特点:都可以操作角标
(1)增
booleanadd(index,element);//指定位置添加元素
BooleanaddAll(index,Collection);//在指定位置增加给定集合中的所有元素,若省略位置参数,则在当前集合的后面依次添加元素
(2)删
Booleanremove(index);//删除指定位置的元素
(3)改
set(index,element);//修改指定位置的元素。
(4)查
get(index);//通过角标获取元素
subList(from,to);//获取部分对象元素
(5)其他
listIterator();//List特有的迭代器
indexOf(obj);//获取元素第一次出现的位置,如果没有则返回-1
注:List集合判断元素是否相同,移除等操作,依据的是元素的equals方法。
List集合在取元素的时候可以用for循环遍历,也可以用Iterator,如果遍历过程中要增删改查,要用ListIterator;
ListIterator是List集合特有的迭代器,是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。所以在迭代器时,只能用迭代器的方法操作元素。可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作。如果想要其他的操作,如添加、修改等,就需要使用其子接口:ListIterrator。该接口只能通过List集合的ListIterator方法获取。
ListIterator特有的方法
add(obj);//增加
set(obj);//修改为obj
hasPrevious();//判断前面有没有元素
previous();//取前一个元素
举例:
package one;
import java.util.*;
import java.util.Iterator;
import java.util.ListIterator;
public class ListDemo {
//打印
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args) {
// 新建一个容器
ArrayList a1 = new ArrayList();
//添加元素
a1.add("java01");
a1.add("java02");
a1.add("java03");
a1.add("java04");
sop("原集合是:"+a1);
//在指定位置添加元素;
a1.add(2, "wanghuitao");
//删除指定位置的元素;
a1.remove(3);
//修改角标获取元素。
a1.set(0, "hahha");
//获取所有元素,只要有角标,就可以用for循环遍历
for(int i =0;i<a1.size();i++)
{
sop("a1("+i+")"+a1.get(i));
}
sop(a1.get(3));
sop(a1);
/*Iterator it = a1.iterator();
while(it.hasNext())
{
Object obj=it.next();
if(obj.equals("java02"))
it.remove();
}*/
ListIterator li=a1.listIterator();
while(li.hasNext())
{
Object obj=li.next();
if(obj.equals("java02"))
li.set("wode");
}
sop("迭代器遍历:"+a1);
}
}
package one;
import java.util.*;
/*
* 去除Arraylist集合中的重复元素;
* */
public class ArrayListTest {
//打印
public static void sop(Object obj)
{
System.out.println(obj);
}
//去重
public static ArrayList singleElement(ArrayList al)
{
//定义一个临时容器
ArrayList newAl = new ArrayList();
Iterator it = al.iterator();
while(it.hasNext())
{
Object obj=it.next();
//用contains判断是否有重复元素,没有添加到新容器中;
if(!newAl.contains(obj))
newAl.add(obj);
}
return newAl;
}
public static void main(String args[])
{
ArrayList al = new ArrayList();
al.add("java01");
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java03");
sop(singleElement(al));
}
}
package one;
import java.util.*;
/*
* 去除Arraylist集合中的重复元素;
* */
public class ArrayListTest {
//打印
public static void sop(Object obj)
{
System.out.println(obj);
}
//去重
public static ArrayList singleElement(ArrayList al)
{
//定义一个临时容器
ArrayList newAl = new ArrayList();
Iterator it = al.iterator();
while(it.hasNext())
{
Object obj=it.next();
//用contains判断是否有重复元素,没有添加到新容器中;
if(!newAl.contains(obj))
newAl.add(obj);
}
return newAl;
}
public static void main(String args[])
{
ArrayList al = new ArrayList();
al.add("java01");
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java03");
sop(singleElement(al));
}
}
比较数组去重的方法:
public class QuChongyByArr {
public static void main(String[] args) {
int[] nums = { 4, 2, 4, 6, 1, 2, 4, 7, 8 };
int[] newArr = new int[10];
int flag = 0;//判断新数组是否存在相同的值
int pos = 0;//新数组的角标
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < newArr.length; j++) {// 判断nums[i]是否已在newArr中
if (nums[i] == newArr[j]) {
flag=1;
break;//跳出内层循环,已经相等,就没必要遍历newArr后面的数据;
}
}
if(flag==1)
flag=0;
else
{
newArr[pos]=nums[i];
pos++;
}
}
System.out.print("[");
for (int i = 0; i < pos; i++) {
System.out.print(newArr[i] + ",");
}
System.out.println("]");
}
}
package day05;
import java.util.ArrayList;
import java.util.Iterator;
/*
将自定义对象作为元素存到ArrayList集合里,去除除重复元素
*/
class Person {
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;
}
//复写object类中的object方法,给出比较标准,而基本数据类型已经覆盖过,所以不需要复写
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
else {
Person p = (Person) obj;
return this.getName() == p.getName() && this.getAge() == p.getAge();
}
}
}
public class ArrayListTest {
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;
}
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(new Person("lisi01", 23));
al.add(new Person("lisi02", 23));
al.add(new Person("lisi01", 34));
al.add(new Person("lisi01", 23));
al.add(new Person("lisi02", 23));
ArrayList ab=singleElement(al);
Iterator it =ab.iterator();
while (it.hasNext()) {
Person p = (Person) it.next();//使用子类特有方法,对象向下转型,
System.out.println(p.getName() + "::" + p.getAge());
}
}
}
3.LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。
特有方法:
(1)增
addFirst();// 在头部添加
addLast();//在尾部添加
(2)获取
//获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
getFirst();
getLast();
(3)删
//获取元素,并删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst();
removeLast();
在JDK1.6以后,出现了替代方法。
(1)增
offFirst();
offLast();
(2)获取
//获取元素,但是不删除。如果集合中没有元素,会返回null。
peekFirst();
peekLast();
(3)删
//获取元素,并删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
举例:
package one;
import java.util.LinkedList;
/*
使用linkList模拟一个堆栈或者队列数据结构;
堆栈:先进后出;
队列:先进先出;
*/
//模拟堆栈
class DuiLie
{
private LinkedList link;
//初始化一个链表容器
DuiLie()
{
link=new LinkedList();
}
public void myAdd(Object obj)
{
//添加列表头部
link.addFirst(obj);
}
//结果:先进后出
public Object myGet()
{
return link.removeFirst();
}
//结果:先进先出
public Object myGet_1()
{
return link.removeLast();
}
public boolean isNull()
{
return link.isEmpty();
}
}
public class LinkList {
public static void main(String[] args) {
DuiLie d = new DuiLie();
d.myAdd("java01");
d.myAdd("java02");
d.myAdd("java03");
d.myAdd("java04");
System.out.println("模拟栈--先进后出");
while(!d.isNull())
{
System.out.println(d.myGet());
}
/*队列:先进先出
* while(!d.isNull())
{
System.out.println(d.myGet_1());
}*/
}
}
四、Set集合
1. Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
|--HashSet:底层数据结构是哈希表。线程不同步。保证元素唯一性的原理:判断元素的hashCode值是否相同。如果相同,还会继续判断元素的equals方法,是否为true。
|--TreeSet:可以对Set集合中的元素进行排序。默认按照字母的自然排序。底层数据结构是二叉树。保证元素唯一性的依据:compareTo方法return 0。
Set集合的功能和Collection是一致的。
2. HashSet:线程不安全,存取速度快。
可以通过元素的两个方法,hashCode和equals来完成保证元素唯一性。如果元素的HashCode值相同,才会判断equals是否为true。如果元素的hashCode值不同,不会调用equals。
注意:HashSet对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
举例:
package two;
import java.util.HashSet;
import java.util.Iterator;
/*
往hashSet集合中存入自定义对象,
姓名和年龄相同为同一个人,重复元素,去除重复元素
思路:
1.对人描述,将人的一些属性等封装进对象;
2.定义一个hashSet容器,存入对象
3.取出;
*/
class Person3
{
private String name;
private int age;
Person3(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int hashCode()
{
//return 20;
//按照条件设置hashcode值,字符串也有自己的哈希值,字符串对象有自己的hashcode方法,这样可以减少比较的次数。
//乘以4是为了避免那个对象的名字跟hashcode组合碰巧和相等
return this.name.hashCode()+age*4;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Person3))
return false;
else
{
Person3 p = (Person3)obj;
//System.out.println(this.name+"...."+p.name);
return this.name.equals(p.name)&&this.age==p.age;
}
}
}
public class HashSetDemo {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person3("a1",11));
hs.add(new Person3("a2",12));
hs.add(new Person3("a3",13));
hs.add(new Person3("a2",12));
System.out.println(hs.remove(new Person3("a3",13)));
Iterator it = hs.iterator();
while(it.hasNext())
{
Person3 p = (Person3)it.next();
System.out.println(p.getName()+"....."+p.getAge());
}
System.out.println("a1:"+hs.contains(new Person3("a1",11)));
}
}
3.TreeSet特点
a、底层的数据结构为二叉树结构(红黑树结构)
b、可对Set集合中的元素进行排序,是因为:TreeSet类实现了Comparable接口,该接口强制让增加到集合中的对象进行了比较,需要复写compareTo方法,才能让对象按指定需求(如人的年龄大小比较等)进行排序,并加入集合。
java中的很多类都具备比较性,其实就是实现了Comparable接口。
注意:排序时,当主要条件相同时,按次要条件排序。
c、保证数据的唯一性的依据:通过compareTo方法的返回值,是正整数、负整数或零,则两个对象较大、较小或相同。相等时则不会存入。
*****Tree排序的两种方式
(1)第一种排序方式:自然排序
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也被称为元素的自然顺序,或者叫做默认顺序。
举例:
package day05;
import java.util.*;
//学生类
class Student implements Comparable
{
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;
}
//复写compareTo以便TreeSet集合调用
public int compareTo(Object obj)
{
Student s=(Student)obj;
if(this.age==s.age)
return this.name.compareTo(s.name);
return this.age-s.age;
//return new Integer(this.age).compareTo(new Integer(s.age));
}
}
public class TreeSetTest
{
public static void main(String[] args)
{
TreeSet<Student> t=new TreeSet<Student>();
t.add(new Student("lisi1",12));
t.add(new Student("abd",2));
t.add(new Student("do",19));
t.add(new Student("od",19));
Iterator<Student> it=t.iterator();
while(it.hasNext())
{
Student s=it.next();
System.out.println(s.getName()+"....."+s.getAge());
}
}
}
(2)第二种方式:比较器
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
比较器构造方式:定义一个类,实现Comparator接口,覆盖compare方法。
当两种排序都存在时,以比较器为主。
举例:
package two;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class Student2
{
private String name;
private int age;
Student2(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
//定义比较器,先按姓名的自然排序,再排年龄
class MyCompare implements Comparator
{
public int compare(Object o1,Object o2)
{
Student2 s1= (Student2)o1;
Student2 s2=(Student2)o2;
int num=s1.getName().compareTo(s2.getName());
if(num==0)
{
return new Integer(s1.getAge()).compareTo(s2.getAge());
}
return num;
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyCompare());
ts.add(new Student2("lisi02",22));
ts.add(new Student2("lisi007",20));
ts.add(new Student2("lisi09",19));
ts.add(new Student2("lisi06",18));
ts.add(new Student2("lisi007",26));
Iterator it = ts.iterator();
while(it.hasNext())
{
Student2 stu = (Student2)it.next();
System.out.println(stu.getName()+"...."+stu.getAge());
}
}
}
五、Map
1.简述
Map<K,V>集合是一个接口,和List集合及Set集合不同的是,它是双列集合,并且可以给对象加上名字,即键(Key)
2特点:
1)该集合存储键值对,一对一对往里存
2)要保证键的唯一性。
3.map集合的框架
Map
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。JDK1.0,效率低。
|--HashMap:底层是哈希表数据结构。允许使用null键null值,该集合是不同步的。JDK1.2,效率高。
|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给Map集合中的键进行排序。
Map和Set很像。其实Set底层就是使用了Map集合。
4.Map常用方法
(1)添加
put(K key,V value);//添加元素,如果出现添加时,相同的键,那么后添加的值会覆盖原有键对应值,并put方法会返回被覆盖的值。
voidputAll(Map <? extends K,? extends V> m);//添加一个集合
(2)删除
clear();//清空
Vremove(Object key);//删除指定键值对
(3)判断
containsKey(Objectkey);//判断键是否存在
containsValue(Objectvalue)//判断值是否存在
isEmpty();//判断是否为空
(4)获取
get(Object key);//通过键获取对应的值
size();//获取集合的长度
Collection<V>value();//获取Map集合中所以得值,返回一个Collection集合
还有两个取出方法,接下来会逐个讲解:
Set<Map.Entry<K,V>>entrySet();
Set<K> keySet();
注:HashMap集合可以通过get()方法的返回值来判断一个键是否存在,通过返回null来判断。
5.Map集合的两种取出方式
Map集合的取出原理:将Map集合转成Set集合。再通过迭代器取出。
1、Set<K> keySet():将Map中所以的键存入到Set集合。因为Set具备迭代器。所以可以通过迭代方式取出所以键的值,再通过get方法。获取每一个键对应的值。
如图:
package day05;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("02", "zhangsan1");
map.put("03", "zhangsan2");
map.put("01", "zhangsan3");
map.put("02", "zhangsan1");
//先获取map集合的所有键的set集合,keySet()
Set<String> keyset=map.keySet();
//有了set集合,就可以获取其迭代器
Iterator<String> it = keyset.iterator();
while(it.hasNext())
{
String key=it.next();
//有了键值可以通过map集合的get方法获取其对应值;
String value = map.get(key);
System.out.println("key:"+key+",value:"+value);
}
}
}
2、Set<Map.Entry<K,V>> entrySet():将Map集合中的映射关系存入到Set集合中,而这个关系的数据类型就是:Map.Entry
其实,Entry也是一个接口,它是Map接口中的一个内部接口;
interface Map {
public static interface Entry {
public abstract Object getKey();
public abstract Object getValue();
}
}
class HashMap implements Map {
class Ha implements Map.Entry{
public Object getKey(){}
public Object getValue(){}
}
}
package day05;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class MapDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("02", "zhangsan1");
map.put("03", "zhangsan2");
map.put("01", "zhangsan3");
map.put("02", "zhangsan1");
/*//先获取map集合的所有键的set集合,keySet()
Set<String> keyset=map.keySet();
//有了set集合,就可以获取其迭代器
Iterator<String> 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集合中的映射关系取出,存入到set集合中。
Set<Map.Entry<String, String>> entryset=map.entrySet();
Iterator<Map.Entry<String, String>> it = entryset.iterator();
while(it.hasNext())
{
//获取的值就是泛型的值
Map.Entry<String, String> me = it.next();
String key=me.getKey();
String value=me.getValue();
System.out.println("key:"+key+",value:"+value);
}
}
}
注意: Map是一个接口,其实,Entry也是一个接口,它是Map的子接口中的一个内部接口,就相当于是类中有内部类一样。为何要定义在其内部呢?
原因:a、Map集合中村的是映射关系这样的两个数据,是先有Map这个集合,才可有映射关系的存在,而且此类关系是集合 的内部事务。
b、并且这个映射关系可以直接访问Map集合中的内部成员,所以定义在内部。
Map集合的练习题:
练习1
package xin;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/*
*每个学生都有对应的归属地
*学生student, 地址String
*学生属性:姓名,年龄;
*注意:姓名和年龄相同的视为同一个学生。
*保证学生的唯一性。
*
*思路:
*1.描述学生类
*2.定义一个map集合,存储学生对象和地址值
*3.获取map中的元素
*/
//.描述学生类
class Student1 implements Comparable<Student1>
{
//描述学生属性
private String name;
private int age;
//学生初始化
Student1(String name,int age)
{
this.name = name;
this.age=age;
}
//获得学生姓名
public String getName()
{
return name;
}
//设置学生姓名
public int getAge()
{
return age;
}
//复写hashcode方法
public int hashCode()
{
return name.hashCode()+age*3;
}
//复写equals方法
public boolean equals(Object obj)
{
if(obj instanceof Student1)
return false;
Student1 s =(Student1)obj;
return this.name==s.name&&this.age==s.age;
}
//先按年龄排序,再按姓名排序
public int compareTo(Student1 s) {
int num=new Integer(this.age).compareTo(s.age);
if(num==0)
return this.name.compareTo(s.name);
return num;
}
//复写tostring()方法
public String toString()
{
return name+":"+age;
}
}
public class MapTest {
public static void main(String[] args) {
//定义一个hashMap集合
HashMap<Student1,String> hm = new HashMap<Student1,String>();
//添加学生对象和地址值
hm.put(new Student1("lisi1",21), "beijing");
hm.put(new Student1("lisi2",22), "shangai");
hm.put(new Student1("lisi3",23), "wuhan");
hm.put(new Student1("lisi4",24), "nanjing");
//第一种取出方式;keyset,先取出所有键的值
Set<Student1> keySet = hm.keySet();
//获得迭代器
Iterator<Student1> it = keySet.iterator();
while(it.hasNext())
{
//迭代器去下一个元素
Student1 stu=it.next();
//通过集合的get方法获取vlaue值
String addr = hm.get(stu);
System.out.println(stu+"......"+addr);
}
//第二种:entrySet
//先取出映射关系集合
Set<Map.Entry<Student1, String>> entrySet =hm.entrySet();
//获取迭代器
Iterator<Map.Entry<Student1, String>> iter = entrySet.iterator();
while(iter.hasNext())
{
//取下一个映射关系
Map.Entry<Student1, String> me = iter.next();
//通过映射关系集合的get方法获取键和值
Student1 stu=me.getKey();
String addr=me.getValue();
System.out.println(stu+"----"+addr);
}
}
}
练习2:
package xin;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/*
获取字符串中的字母出现的次数,
分析:每个字母都有次数,说明字母和次数之间有映射关系。
*/
public class MapTest3 {
public static void charCount(String str)
{
//将字符串转化为字符数组
char[] chs =str.toCharArray();
//定义一个TreeMap集合用于存储字母和字母出现的次数,TreeMap集合会给字母自动排序
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
//遍历数组
for(int i =0;i<chs.length;i++)
{
// 获取键值
Integer value = tm.get(chs[i]);
// 如果键值为空,键值置为1
if(value==null)
{
tm.put(chs[i], 1);
}
// 否则,键值加1,存入集合
else
{
value = value+1;
tm.put(chs[i], value);
}
}
//System.out.println(tm);
//建立缓冲区
StringBuilder sb = new StringBuilder();
//获取映射关系的集合
Set<Map.Entry<Character,Integer>> entrySet =tm.entrySet();
//获取迭代器
Iterator<Map.Entry<Character,Integer>> it = entrySet.iterator();
while(it.hasNext())
{
//获取下一个映射关系
Map.Entry<Character,Integer> me = it.next();
//获取键
Character ch = me.getKey();
//获取值
Integer value = me.getValue();
System.out.print(ch+"("+value+")");
}
}
public static void main(String[] args) {
charCount("ackidid");
}
}
练习3
package xin;
import java.util.*;
/*在很多项目中,引用比较多的是一对多的映射关系,这就可以通过嵌套的形式将多个映射定义到一个大的集合中,并将大的集合分级处理,形成一个体系。
班级对应学生,学生类中学号对应姓名
“yure” Student("01","zhangdan")
“yure” Student("02","lisi")
“jiuye” Student("01","wangwu")
“yure” Student("02","zhaojiu")
如同一个学校有多个教室,每个教室都有名称;
**/
//描述学生类
class Student {
//学生属性
String id, name;
//初始化
Student(String id, String name) {
this.name = name;
this.id = id;
}
//自定义tostring方法
public String toString() {
return id + "::" + name;
}
}
public class MapDemo {
public static void main(String[] args) {
//建立czbk的结合
Map<String, ArrayList<Student>> czbk = new HashMap<String, ArrayList<Student>>();
//建立班级集合,一个jiuye,一个yure
ArrayList<Student> yure = new ArrayList<Student>();
ArrayList<Student> jiuye = new ArrayList<Student>();
//添加班级实体
czbk.put("yureban", yure);
czbk.put("jiuyeban", jiuye);
//班级实体添加学生
yure.add(new Student("01", "zhangsan"));
yure.add(new Student("02", "lisi"));
jiuye.add(new Student("01", "wnagwu"));
jiuye.add(new Student("02", "zhaoliu"));
//获取迭代器,获得锁有房间名
Iterator<String> it1 = czbk.keySet().iterator();
while (it1.hasNext()) {
String roomname = it1.next();
System.out.println(roomname);
ArrayList<Student> al = czbk.get(roomname);
//获取迭代器,遍历学生对象
Iterator<Student> it2 = al.iterator();
while (it2.hasNext()) {
//通过迭代器获取每一个学生对象
Student w = (Student) it2.next();
//打印
System.out.println(w.toString());
}
}
}
}
关于集合的总结:
集合总结:
集合的一些技巧:
需要唯一吗?
需要:Set
需要制定顺序:
需要: TreeSet
不需要:HashSet
但是想要一个和存储一致的顺序(有序):LinkedHashSet
不需要:List
需要频繁增删吗?
需要:LinkedList
不需要:ArrayList
如何记录每一个容器的结构和所属体系呢?
看名字!
List
|--ArrayList
|--LinkedList
Set
|--HashSet
|--TreeSet
后缀名就是该集合所属的体系。
前缀名就是该集合的数据结构。
看到array:就要想到数组,就要想到查询快,有角标.
看到link:就要想到链表,就要想到增删快,就要想要 add get remove+frist last的方法
看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashcode方法和equals方法。
看到tree:就要想到二叉树,就要想要排序,就要想到两个接口Comparable,Comparator 。
而且通常这些常用的集合容器都是不同步的。
Map:一次添加一对元素。Collection 一次添加一个元素。
Map也称为双列集合,Collection集合称为单列集合。
其实map集合中存储的就是键值对。
map集合中必须保证键的唯一性。
1.枚举:让某个类型的变量的取值只能为若干个固定值中的一个。
2.特点:(1)枚举类是一个特殊的类,每个元素都是该类的一个实例对象,用enum关键字定义枚举类;
(2)枚举类型继承过java.lang.Enum类
(3)枚举类没有public的构造函数
(4)枚举值是public statiche final的
举例:
package day09;
//定义枚举类,
enum Color
{
red,green,blue;
}
//定义花
class Flower
{
private String name;
Color color;
public Flower(String name, Color color) {
super();
this.name = name;
this.color = color;
}
@Override
public String toString() {
return "Flower [name=" + name + ", color=" + color + "]";
}
}
public class EnumDemo {
public static void main(String[] args) {
Flower f = new Flower("moli",Color.red);
System.out.println(f);
}
}
二、静态导入
可以使被导入的静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出它们的类名
例如:import staticjava.util.Arrays.*;
注意:当类名重名时,需要指定具体的包名。
当方法重名时,指定具备所属的对象或者类。
三、增强for循环
1.格式:for(数据类型变量名 :被遍历的集合(collection)或者数组) {执行语句}
2 .注意:对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。可以看作是迭代器的简写形式
3 .传统for和高级for的区别:
高级for有一个局限性。必须有被遍历的目标(集合或数组)。
传统for遍历数组时有索引。
建议在遍历数组的时候,还是希望使用传统for。因为传统for可以定义角标。
举例:
package day09;
import java.util.*;
public class ForDemo
{
public static void main(String[] args)
{
//定义一个ArrayList集合
ArrayList<String> al = new ArrayList<String>();
al.add("lisi1");
al.add("lisi2");
al.add("lisi3");
//用增强for遍历集合
for(String s : al)
{
System.out.println(s);
}
//传统for与 增强for区别
int[] arr = {23,2,5,4};
for(int i=0; i<arr.length; i++)
{
System.out.println(arr[i]);
}
for(int i : arr)
{
System.out.println("i:"+i);
}
}
}
四 、可变参数
1.如果一个方法在参数列表中传入多个参数,个数不确定,那么每次都要复写该方法。这时可以用数组作为形式参数。但是在传入时,每次都需要定义一个数组对象,作为实际参数。在JDK1.5版本后,就提供了一个新特性:可变参数。
2. 用…这三个点表示,且这三个点位于变量类型和变量名之间,前后有无空格皆可。
3.可变参数其实就是数组参数的简写形式。不用每一次都手动的建立数组对象。只要将要操作的元素作为参数传递即可。隐式将这些参数封装成了数组。
在使用时注意:可变参数一定要定义在参数列表的最后面。
举例:
package day09;
import java.util.*;
public class ForDemo {
public static void main(String[] args) {
show(3, 4, 5,6,5,4,3);
}
private static void show(int ...a) {
for(int i = 0; i<a.length;i++){
System.out.print(a[i]+" ");
}
}
}
五、基本数据类型的自动拆箱与装箱
1.自动装箱,拆箱
Integer x = new Integer(5);
等价于:Integer x = 5;//自动装箱,new Integer;
x = x+2;//x进行自动拆箱,变成了int类型,和2运算,再将和进行装箱赋给x
注意:对于基本数据类型的说明:整数在-128 ~ 127之间的数,包装成Integer类型对象,会存入常量池中的缓存,再创建一个对象的时候,如果其值在这个范围内,就会直接到常量池中寻找,因为这些小数值使用的频率很高,所以缓存到常量池中,被调用时就方便很多。
六 泛型:
泛型:
jdk1.5出现的安全机制。
好处:
1,将运行时期的问题ClassCastException转到了编译时期。
2,避免了强制转换的麻烦。
<>:什么时候用?当操作的引用数据类型不确定的时候。就使用<>。将要操作的引用数据类型传入即可.
其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型 。
泛型技术是给编译器使用的技术,用于编译时期。确保了类型的安全。
运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?因为为了兼容运行的类加载器。
泛型的补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者在强制转换了。
泛型的通配符:? 未知类型。
泛型的限定:
? extends E: 接收E类型或者E的子类型对象。上限
一般存储对象的时候用。比如 添加元素 addAll.
? super E: 接收E类型或者E的父类型对象。 下限。
一般取出对象的时候用。比如比较器。