-----------android培训、java培训、java学习型技术博客、期待与您交流!---------
集合类
首先我们来看一个集合的关系图
(因为是网上直接有的,我就不自己画了,浪费时间,嘿嘿)
为什么出现集合类?
面向对象语言对实物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,
集合就是存储对象最常用的一种方式。(对象多了用集合存,数据多了用对象存)
数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的
集合长度是可变的,数组中可以存储基本数据类型,集合只能存储对象。
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
每一个容器因为数据结构不同,所以进行了单独的划分。因为他们自身的特点不同
在最先展示的java中集合类的关系图中,可以看到第一行的 Iterator,Collection,Map三个接口。其中Iterator是迭代器,用来遍历集合的,先不介绍。
最先开始了解Collection接口,它又有两个子接口List,Set。我们先来学习父类,因为父类是通过不断抽取得来的,有子类的共性,
先学会了共性方法,再去学习子类的特有方法。
Collection
所属关系
Collection
|----List //元素是有序的,可以重复,因为List体系内有索引
|----Set //元素是无序的,不可以重复
对象中存储的都是对象的引用(地址)。
Collection接口的常见操作
1. 添加元素
boolean add(Object obj) //add方法的参数类型是Object,以便于接收任意类型对象。
2. 获取个数,集合长度
int size() //获取集合内元素的个数
3. 删除元素
boolean remove(Object obj) //从集合中移除指定元素的单个实例,如果删除成功返回true
void clear() //移除这个集合中所有的元素
4. 判断元素
boolean contains(Object obj); //如果此集合中包含obj元素,返回true
boolean isEmpty(); //判断这个集合是否为空
5. 交集
boolean removeAll(Collection<?> c); //<>一会儿会讲,这个集合保留和c集合不同的元素
boolean reainAll(Collection<?> c); //这个集合保留和c集合相同的元素
迭代
刚才我们提过一句的迭代器Iterator,迭代是取出集合中元素的一种方式。
(对于取出,不只一个动作的话,就将取出这个动作封装成为一个对象,对于取出不足以用一个动作来描述,
需要多个功能来体现,就把多个功能封装进一个对象中,因为数据结构不同,每个取出对象取出的实现方式也不一样,那么取出就需要被描述,
通过类来描述,类就定义在了集合内部,因为元素在集合中,操作的是元素,直接操作元素最方便的就是内部类,因为在集合内部才做最方便,
内部类就完成取出动作,每个容器都有内部类,内部类都有一个共性,会判断,判断是否有元素)
把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类,
而每个容器的数据结构不同,所以取出的动作细节也不一样,但是都有共性内容,判断和取出,
那么可以将这些共性抽取一个规则(这些内部类中抽取一个规则),这个规则就是Iterator
如何获取集合的取出对象呢?
通过一个对外提供的方法:iterator();
迭代的常见操作
boolean hasNext() //如果还有下一个元素,返回true,可以作为循环条件
E next() //返回迭代的下一个元素
void remove() //移除迭代器返回的最后一个元素
注:在迭代时循环中next调用一次,就要hasNext判断一次。
迭代器的使用方法示例
/*
这里我用到的东西可能前面没有讲过,因为我是学完了所有课程回来复习
写的博客,所以为了养成好的习惯,我还是使用完整的代码。
其实这些代码都是我用EditPlus写的,虽然我已经回用eclipes了,但是觉得这样写比较好
不会有自动联想,也不会有错误提示,可以增强手写代码的能力,建议初学者也是,后期使用eclipse比较好
我现在博客写到这人,敲代码真的比以前顺畅多了,亲测有效!!哈哈
话不多说,来看Iterator使用的实例
*/
import java.util.*;
class IteratorDmeo
{
public static void main(String[] args)
{
//先创建一个集合
Collection<String> c = new ArrayList<String>();
//往集合中添加元素
c.add("潘机智1号");
c.add("潘机智2号");
c.add("潘机智3号");
c.add("潘机智4号");
c.add("潘机智5号");
//输出集合
System.out.println(c);
//方法1 方法2 都是通过迭代器获取集合中的元素,不同的是迭代器获取的位置
//方法1 在循环外单独获取
//方法2 在循环体上获取
//方法1
//获取一个迭代去
System.out.println("方法1");
//获取一个迭代器对象
Iterator<String> it1 = c.iterator();
//建立一个循环,hasNext()语句作为循环条件
while(it1.hasNext())
{
//输出集合
System.out.println(it1.next());
}
//方法2
System.out.println("方法2");
//在for循环的初始化表达位置,获得迭代器对象
for(Iterator<String> it2 = c.iterator() ; it2.hasNext();)
{
System.out.println(it2.next());
}
}
}
运行结果
List
组成关系
List //元素是有序的,元素可以重复,因为该集合体系有索引。
|----ArrayList //底层的数据结构是数组结构。
|----LinkedList //底层的数据结构是表链数据结构。
|----Vector //底层的数据结构是数组数据结构。
List的特有方法
凡是可以操作角标的方法都是该体系特有的方法
1. 增
void add(int index , E element) 在列表指定位置插入指定元素
boolean addAll(int index , Collection<? extends E> c) 将Collection中的元素都插入到列表中的指定位置
2. 删
E remove(int index) 移除列表中指定位置的元素
3. 改
E set(int index , E element) 用指定元素替换列表中指定位置的元素
4. 查(获取)
E get(int index) 返回列表中指定位置的元素
List<E> subList(int fromIndex , int toIndex) 返回列表中从指定位置开始到指定位置结束的元素的集合(包头不包尾)
ListIterator<E> listIterator() 返回此列表元素的列表迭代器
int indexOf(Object obj) 获取obj元素第一次出现的位置,没有则返回-1
List集合特有的迭代器
ListIterator
它是Iterator的子接口,在迭代时不可以通过集合对象的方法操作集合中的元素,因为会发生并发修改异常。
所以在迭代时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,
如果想要其他的操作如:添加,修改等,就需要使用其子接口,ListIterator。该接口只能通过List集合的listIterator()方法获取。
ListIterator出现以后可以对集合进行在遍历的过程中的增删改查。
ListIterator的特有方法
1. void add(E e) 将指定元素插入列表
2. void set(E e) 用指定元素替换next()或者previous()返回的最后一个元素
3. boolean hasPrevious() 逆向遍历列表,列表迭代器有多个元素,返回true
4. E previous() 返回列表中的前一个元素
面试题:
List集合具体对象的特点
ArrayList:查询速度快,但是增删稍慢,线程不同步。
LinkedList:增删速度很快,查询稍慢。
Voector:线程同步,被ArrayList替代了。
ArrayList和Vectot的区别
数组是固定长度,但集合是可变长度,ArrayList和Vectot默认的长度都为10,当元素个数超过10个,
还需要往里添加元素时就会new一个新的集合。ArrayList为50%延长,也就是变为了15。Vctor为100%延长,也就是变为了20.
把原来数组中的元素copy到新集合中,再把新加进来的元素添加进去,所以相比之下ArrayList比较节省空间。
Vertor
枚举(Enumeration)是Vector特有的取出方式。
发现枚举和迭代器很像。其实枚举和迭代器是一样的,因为枚举的名称以及方法的名称都过长,所以被迭代器取代了。
特有方法
1. void addElement(E obj) 添加元素,相当于add(obj)
2. Enumeration<E> elements() 返回此向量的组件的枚举,获取美剧对象的方法
3. boolean hasMoreElements() 相当于Iterator的hasNext()方法
4. E nextElement() 相当于Iterator的newt()方法
用枚举取出Vetor集合中元素示例
Collection<E> c = new Vertor<E>();
for(Enumeration<E> e =c.elements() ; e.hasMorElements() ; ) {
System.out.println(e.nextElement());
}
LinkedList
底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。
特有方法
1. void addFirst(E e) 将指定元素插入该列表的开头
2. void addLast(E e) 将指定元素添加到该列表的结尾
3. E getFirst() 返回此列表的第一个元素
4. E getLast() 返回此列表的最后一个元素
3,4两个方法,获取元素,但不删除元素,如果集合中没有元素会出现NoSuchElmentException
5. E removeFirst() 返回并移除此列表的第一个元素
6. E removeLast() 返回并移除此列表的最后一个元素。
5,6两个方法,获取元素,元素会被删除,如果集合中没有元素会出现NoSuchElmentException
在JDK1.6版本之后出现了替代方法
为了使博客不那么拖拉,我就按照上面的方法顺序写,用法是一样的,有不一样的会写明
1. boolean offerFirst() 将指定元素插入该列表的开头
2. boolean offerLast() 将指定元素添加到该列表的结尾
3. E peeckFirst() 返回此列表的第一个元素,列表为空返回null
4. E peekLast() 返回此列表的最后一个元素,列表为空返回null
5. E poolFirst() 返回并移除此列表的第一个元素,列表为空返回null
6. E poolLast() 返回并移除此列表的最后一个元素,列表为空返回null
ArrayList练习
/*
ArrayList练习
将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如定义一个Person类,同姓名同年龄视为一个人,是相同元素
*/
import java.util.*;
//先创建person类
class Person
{
//Person类中有两个属性,name age
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;
}
//复写equals方法,定义姓名和年龄相同的是同一个对象。
public boolean equals(Object obj)
{
//先来判断用户传入的obj是否是Person或者Person的子类
if(!(obj instanceof Person))
//如果不是,返回false
return false;
//如果传入的obj是Person或者它的子类运行以下代码
//将obj转成Person类
Person p = (Person)obj;
//比较调用equals方法的对象和p对象的姓名和年龄是否相同,并返回结果
//因为name是String类型的,所以可以直接调用equals,String类复写了
//equals方法,使它只比较字符串的内容是否相同,年龄用==比较即可
return this.name.equals(p.name) && this.age == p.age;
}
}
class ArrayListTest
{
public static void main(String[] args)
{
//创建一个ArrayList集合
ArrayList<Person> cal = new ArrayList<Person>();
//往集合中添加元素
cal.add(new Person("张三",45));
cal.add(new Person("李四",31));
cal.add(new Person("王五",14));
//跟第一个同岁的
cal.add(new Person("赵六",45));
//跟第一个完全相同的
cal.add(new Person("张三",45));
//根第一个同名的
cal.add(new Person("张三",19));
//调用获取唯一元素的方法
cal = singleElement(cal);
//迭代集合并打印集合中的元素,来看一看是否把重复的元素加进去了
for(Iterator<Person> it = cal.iterator() ; it.hasNext() ;)
{
Person p = it.next();
//通过Person类的getName 和getAge方法获取姓名年龄并打印结果
System.out.println(p.getName()+"........"+p.getAge());
}
}
//获取唯一元素的方法
public static ArrayList<Person> singleElement(ArrayList<Person> cal)
{
//新定义一个集合
ArrayList<Person> newCal = new ArrayList<Person>();
//迭代参数传入的集合
for(Iterator<Person> it = cal.iterator(); it.hasNext();)
{
//用Person类型变量接收 读取到的集合中的元素
Person p = it.next();
//判断一个元素是否已经在newCal集合中,就是在比较集合中有没有和newCal一样的。
if(!newCal.contains(p))
{
//如果不存在,再往里添加
newCal.add(p);
}
}
//返回新数组
return newCal;
}
}
运行结果
LinkList练习
/*
LinkList练习
因为毕老师的例子比较经典,就用老师的例子。
但是用我自己的想法写出来
使用LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出 如同一个杯子。先倒进去的水最后才能喝到
队列:先进先出 First in First out FIFO 如同一个水管。先进去的水会先出来
*/
import java.util.*;
class LinkedListTest
{
public static void main(String[] args)
{
//先创建一个LinkedList集合
LinkedList<String> l = new LinkedList<String>();
//往里面按顺序的添加元素
l.add("水一号---1");
l.add("水二号---2");
l.add("水三号---3");
l.add("水四号---4");
//调用模拟堆栈方法
duiZhan(l);
//模拟队列方法
//duiLie(l);
}
//模拟堆栈方法,先进后出,先存进去的元素最后输出
public static void duiZhan(LinkedList<String> l)
{
//创建一个循环,集合里是否还有元素是循环条件
//集合里没有元素了循环停止
while(!l.isEmpty())
{
//使用LinkedList的特有方法,removeLast()
//获取并且删除集合里的最后一个元素。然后打印该元素
System.out.println(l.removeLast());
}
}
//模拟队列方法,先进先出,先存进去的元素最先输出
public static void duiLie(LinkedList<String> l)
{
//创建一个循环,集合里是否还有元素是循环条件
//集合里没有元素了循环停止
while(!l.isEmpty())
{
//使用LinkedList的特有方法,removeFirst()
//获取并且删除集合里的第一个一个元素。然后打印该元素
System.out.println(l.removeFirst());
}
}
}
模仿堆栈的运行结果
Set
Set:元素是无序的(存入和取出的顺序不一定一致)元素不可重复
|----HashSet:底层数据结构是哈希表,线程是非同步的
|----TreeSet:底层数据结构是二叉树,可以对Set集合中的元素进行排序
Set集合的功能和Collection是一致的。
HashSet
HashSet:线程不安全,存取速度快。
理解:HashSet内的元素排序是地址值,当地址的哈希值一致时,判断是不是两个不同的对象。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode()和equals()方法来完成的
如果元素的HashCode值相等,才会判断equals是否为true,如果元素的HashCode值不相同,不会调用equals。
以上就是HashSet保证元素唯一性的依据。
而且当我们在自定义对象需要往集合里存时,一般都重写hashCode()和equals()方法
注意:对于判断元素是否存在,以及删除等操作,依赖的是元素的hashCode()和equals()方法,
ArrayList依赖equals() HashSet依赖hashCode()和equals()
HashSet练习
/*
HashSet练习
往HashSet集合中存入自定对象
姓名和年龄相同为同一个人,重复元素。
不能存进重复元素
*/
import java.util.*;
//先创建一个Person类
class Person
{
//人有姓名年龄属性
private String name;
private int age;
Person(String name , int age)
{
this.name = name;
this.age = age;
}
//复写hashCode方法,使相同元素的HashCode值相同
public int hashCode()
{
//返回自定义的HashCode值,其中字符串调用的hashCode()是String类的方法
//返回这个字符串的hashCode值
return name.hashCode()+age*18;
}
//复写equals方法
public boolean equals(Object obj)
{
//判断传入的obj是否是Person或者它的子类实例
if(!(obj instanceof Person))
//如果不是返回false
return false;
//把obj转成Person类的实例
Person p = (Person)obj;
//判断调用equals的内容是否和obj中的内容个相同,并返回结果
return this.name.equals(p.name)&& this.age == p.age;
}
//获取姓名的方法
public String getName()
{
return name;
}
//获取年龄的方法
public int getAge()
{
return age;
}
}
class HashSetTest
{
public static void main(String[] args)
{
//创建一个HashSet集合
HashSet<Person> hs = new HashSet<Person>();
//往HashSet集合中存入元素
hs.add(new Person("潘机智1号---1",21));
hs.add(new Person("潘机智2号---2",18));
hs.add(new Person("潘机智3号---3",34));
//和第一个一样
hs.add(new Person("潘机智1号---1",21));
//和第一个名字不一样
hs.add(new Person("潘机智7号---7",21));
//和第一个年龄不一样
hs.add(new Person("潘机智1号---1",99));
//迭代集合
for(Iterator<Person> it=hs.iterator() ; it.hasNext();)
{
//将集合中的元素用Person型的变量接收
Person p = it.next();
//通过Person的方法获得name和age并输出
System.out.println(p.getName()+"...."+p.getAge());
}
}
}
运行结果
TreeSet
底层的数据结构为二叉树结构
特点:
可对Set集合中的元素进行排序,TreeSet类实现了Comparable接口,该接口强制让增加到集合中的对象进行了比较,
需要复写compareTo方法,才能让对象按指定需求(如人的年龄大小比较等)进行排序,并加入集合。
java中的很多类都具备比较性,其实就是实现了Comparable接口。
注意:排序时,当主要条件相同时,按次要条件排序。如姓名一样,就按年龄排序
保证数据的唯一性的依据
通过compareTo方法的返回值,负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。
TresSet排序的两种方式
1. 自然排序
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也被称为元素的自然顺序,或者叫做默认顺序。
TreeSet自然排序示例
/*
TreeSet的第一种排序方式
这里我们还是使用Person作为自定义对象
*/
//Person类实现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;
}
//复写compareTo方法
public int compareTo(Object obj)
{
//判断obj是否是Person或者它子类的实例
if(!(obj instanceof Person))
//如果不是抛出RuntimeException
throw new RuntimeException("不是人对象");
//将obj转成Person型
Person p = (Person)obj;
//判断年龄是否相等
if(this.age == p.age)
//如果相等再比较姓名。其中用到的compareTo方法是String重新复写的
//仅仅用于,按字典顺序比较两个字符串
return this.name.compareTo(p.name);
//如果年龄不相等的话直接返回两个对象的年龄差
return this.age - p.age;
}
//获取姓名的方法
public String getName()
{
return name;
}
//获取年龄的方法
public int getAge()
{
return age;
}
}
class TreeSetTest1
{
public static void main(String[] args)
{
//创建一个TreeSet集合
TreeSet<Person> ts = new TreeSet<Person>();
//往集合中添加元素
ts.add(new Person("pt01",30));
ts.add(new Person("pt002",19));
ts.add(new Person("pt07",30));
ts.add(new Person("pt077",45));
ts.add(new Person("pt008",30));
ts.add(new Person("pt009",35));
//迭代集合,输出元素
for(Iterator<Person> it=ts.iterator() ; it.hasNext();)
{
Person p = it.next();
System.out.println(p.getName()+"...."+p.getAge());
}
}
}
运行结果
2. 比较器
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
比较器构造方式:定义一个类,实现Comparator接口,覆盖compare方法。
当两种排序都存在时,以比较器为主。
TreeSet比较器排序示例
/*
TreeSet的第二种排序方式
依然用Person作为自定义对象
类自带的排序方式不是我们想要的。
我们想要按name来排序
*/
import java.util.*;
//创建Person类实现Comparable接口
class Person implements Comparable
{
private String name;
private int age;
Person(String name , int age)
{
this.name = name;
this.age = age;
}
//复写compareTo方法,以便TreeSet集合调用
public int compareTo(Object obj)
{
//跟上一个程序一样
if(!(obj instanceof Person))
throw new RuntimeException("不是Person类");
Person p = (Person)obj;
if(this.age == p.age)
return this.name.compareTo(p.name);
return this.age - p.age;
}
//获取姓名方法
public String getName()
{
return name;
}
//获取年龄方法
public int getAge()
{
return age;
}
}
//定义一个比较器,比较名字长度排序
class myCompare implements Comparator<Person>
{
//复写compare方法
public int compare(Person p1 , Person p2)
{
//比较姓名的顺序,并返回结果
int num = p1.getName().compareTo(p2.getName());
//判断num是否等于0,如果是表示姓名一样
if(num==0)
{
//再比较年龄,返回结果
return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
}
//返回num
return num;
}
}
class TreeSetTest2
{
public static void main(String[] args)
{
//建立一个TreeSet集合,将比较器作为参数传递给TreeSet的构造函数
TreeSet<Person> ts = new TreeSet<Person>(new myCompare());
//往集合中添加元素
ts.add(new Person("pt01",30));
ts.add(new Person("pta002",19));
ts.add(new Person("pt07",30));
ts.add(new Person("pt077",45));
ts.add(new Person("pt708",30));
ts.add(new Person("pt009",35));
//迭代并打印集合
for(Iterator<Person> it = ts.iterator() ; it.hasNext();)
{
Person p = it.next();
System.out.println(p.getName()+"..."+p.getAge());
}
}
}
运行结果
Map
该集合存储键值对,是双列集合。Map<k , v> 键 key 值 value
特点
该集合存储键值对,一对一对往里存。
要保证键的唯一性
键和值之间的关系是映射关系
Map
|----Hashtable:底层是哈希表数据结构,不可以存入null键,null值的情况,该集合是线程同步的。JDK1.0,效率低。
|----HashMap:底层哈希表数据结构,允许使用null键和null值,该集合不同步,JDL1.2,效率高
|----TreeMap:底层是二叉树数据结构,可以用于给Map集合中的键进行排序
Map和Set很像,其实Set底层就是使用了Map集合。
Map集合常见方法
1. 添加
V put( K key , V value) 将指定的值与此映射中的指定键关联。
当存储了相同的键时,新的值会替换老的值,put方法还会把这个键原来的值返回来。
当在存第一个键时,没有对应的值,返回的是null。put方法会返回这个键原来的值。
个人理解:就像商场里的自助照相机,就是一个键,我进去了里面,没有人就返回null,我朋友要进来,把我挤出来了,
返回的就是我。这个键现在对应的就是我朋友,所以添加元素时,若果出现相同的键,
那么后添加的值会覆盖键对应的原有的值,并且put()方法会返回被覆盖(挤掉)的值,
这也就是为什么put()方法,返回值是V(value参数的类型)的原因。
void putAll(Map<? extends K , ? extends V> m) 从指定映射中将所有的映射关系复制到此映射中
2. 删除
void clear() 从此映射中移除所有映射关系
V remove(Object key) 移除指定键对应的值,并返回这个值,如果此键没有映射关系,返回null
3. 判断
boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回true
boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回true
boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true
4. 获取
V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null
。
可以通过get方法的返回值来判断一个键是否存在,通过是否返回null来判断。
int size() 返回此映射中的键-值映射关系数,也就是集合大小
Collection<V> values 返回此映射中包含的值的Collection视图,也就是说把Map集合中的值都取出,存入一个Collection集合中。
Set<Map.Entry<K,V>> entrySet() 将此Map集合的映射关系返回到一个Set集合中
Set<K> keySet() 将此映射中所有的键返回到一个Set集合中
最后两个方法是用于取出Map集合中元素的,等一下会仔细讲解。
Map集合的两种取出方式
1. Set<K> keyset():将Map中所有的键存入到Set集合中
因为Set具备迭代器,所以Set<K> keySer()方法可以通过迭代方式取出所有的键,再根据get方法获取每一个键对应的值。
方法一示例
/*
Map集合取出元素方式一
在这里我们主要演示取出元素,所以就不定义自定义对象了
*/
import java.util.*;
class MapExtractionTest1
{
public static void main(String[] args)
{
//先创建一个Map集合键是String类型的,值是int型的
Map<String,Integer> map = new HashMap<String,Integer>();
//往集合中添加元素
map.put("pt001",1);
map.put("pt003",3);
map.put("pt004",4);
map.put("pt002",2);
//先使用keySet()方法将Map集合中所有的键取出,存入一个Set集合中
Set<String> s = map.keySet();
//通过迭代器迭代Set集合
for(Iterator<String> it = s.iterator() ; it.hasNext();)
{
//用一个String变量接收key
String k = it.next();
//通过get方法获取键对应的值并用int类型变量接收
int v = map.get(k);
//打印key和value的值
System.out.println("ket:"+k+".....value"+v);
}
}
}
运行结果
2. Set<Map.Entry<K,Y>> entrySet():将Map集合中的映射关系存入到了Set集合中,而这个关系的数据类型就是Map.Entry。
理解:在Map中键值是映射关系,在Set中键值的关系的数据类型就是Map.Entry。键值合起来就是一个Map.Entry的实例对象。
那么关系对象Map.Entry获取到后,就可以通过Map.Entry中的getKey和getValue方法获取关系中的键和值。
Map.Entry到底是什么?
其实Entry是一个接口,它是Map接口中的一个内部接口
理解:因为有了Map类才会有Entry,而且Entry是描述Map内部键值关系的,所以被封装成了内部接口。
方法二示例
/*
Map集合取出元素方式二
用的还是取出方法一的创建Map集合和添加元素的代码。只是取出方式不一样
*/
import java.util.*;
class MapExtractionTest2
{
public static void main(String[] args)
{
//先创建一个Map集合键是String类型的,值是int型的
Map<String,Integer> map = new HashMap<String,Integer>();
//往集合中添加元素
map.put("pt001",1);
map.put("pt003",3);
map.put("pt004",4);
map.put("pt002",2);
//使用entrySet方法取出Map集合中的映射关系,存入Set集合中
Set<Map.Entry<String,Integer>> entrySet = map.entrySet();
//通过iterator()方法得到迭代器的实例对象
Iterator<Map.Entry<String,Integer>> it = entrySet.iterator();
//使用迭代器遍历entrySet集合
while(it.hasNext())
{
//用Map.Entry接收entrySet集合中的元素
Map.Entry<String,Integer> me = it.next();
//使用getKey 和 getValue方法取出键和值
String key = me.getKey();
int value = me.getValue();
//输出键和值
System.out.println("ket:"+key+"=====value"+value);
}
}
}
运行结果
什么时候使用Map集合呢?
当发现有映射关系时,可以选择Map集合,因为Map集合中存放的就是映射关系。
下面来看几个Map集合的练习
练习一
/*
Map集合练习一
每一款手机都有对应的生产国籍
比如,苹果美国生产,三星韩国生产,小米中国生产,诺基亚芬兰生产
手机Phone 产地国 String Country
手机属性:品牌,型号,如果品牌和型号相同视为同一个手机。
手机和产地国有了映射关系,所以可以使用Map集合
步骤
1.描述手机
2.定义Map集合,将手机作为键,生产国籍作为值存入集合中
3.取得集合中的元素
*/
import java.util.*;
//先来描述手机,实现Comparable接口使类具备比较性
class Phone implements Comparable<Phone>
{
//品牌属性
private String brand;
//型号属性
private String model;
//初始化手机
Phone(String brand , String model)
{
this.brand = brand;
this.model = model;
}
//获取品牌方法
public String getBrand()
{
return brand;
}
//获取型号方法
public String getModel()
{
return model;
}
//输出方法
public String toString()
{
return "品牌:"+brand+",型号:"+model;
}
//因为我们要使用HashMap所以要复写hashCode和equals方法
public int hashCode()
{
//使相同品牌和型号的手机HashCode值相同
return brand.hashCode()+model.hashCode()+4*18;
}
//复写equals方法
public boolean equals(Object obj)
{
//判断obj是否是Phone或其子类的实例
if(!(obj instanceof Phone))
//如果不是,抛出RuntimeException使程序停止
throw new RuntimeException("对象类型不匹配");
//如果是运行以下代码
//将obj转成Phone类型
Phone p = (Phone)obj;
//判断品牌和型号是否相同,并返回结果
return this.brand.equals(p.brand) && this.model.equals(p.model);
}
//复写compareTo方法。虽然我的例子中是中文 按自然顺序排序也不知道谁会排在前面
//例子是为了娱乐,但内容必须要有
public int compareTo(Phone p)
{
//使用String的compareTo方法比较品牌的自然顺序,返回值用int类型变量记录
int num = this.getBrand().compareTo(p.getBrand());
//如果num==0
if(num==0)
//那么再比较型号的自然顺序
return this.getModel().compareTo(p.getModel());
//如果num!=0,返回num
return num;
}
}
class MapTest1
{
public static void main(String[] args)
{
//创建一个Map集合
Map<Phone,String> hm = new HashMap<Phone,String>();
//往集合中添加元素
hm.put(new Phone("苹果","iPhone6"),"美国");
hm.put(new Phone("三星","s6"),"韩国");
hm.put(new Phone("小米","红米"),"中国");
hm.put(new Phone("诺基亚","砸核桃神器"),"芬兰");
hm.put(new Phone("魅族","M1"),"中国");
hm.put(new Phone("苹果","iPhone6"),"中国山寨");
//取出集合中的元素
Set<Phone> keySet = hm.keySet();
Iterator<Phone> it = keySet.iterator();
while(it.hasNext())
{
//用Phone类型变量接收key
Phone key = it.next();
//用get方法获取value
String value = hm.get(key);
//输出键和值
System.out.println(key+",产地:"+value);
}
}
}
运行结果
练习二
/*
Map集合练习二
对学生对象的年龄进行升序排序。
学生都有自己的籍贯,所以
key = 学生 value = 籍贯
因为学生和籍贯是以键值对的形式存在的,所以用Map集合
在这里我们使用TreeSet
*/
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 String toString()
{
return name+"..."+age;
}
//具体的复写过程我就不多讲解了,复写了这么多次,大家应该懂了~~
//复写hashCode方法
public int hashCode()
{
return name.hashCode()+age*18;
}
//复写equals方法
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException("类型不批陪");
Student s = (Student)obj;
return this.name.equals(s.name) && this.age == s.age;
}
}
class MapTest2
{
public static void main(String[] args)
{
//创建Map集合对象,并把比较器对象作为参数传入
Map<Student,String> tm = new TreeMap<Student,String>(new myComparator());
//添加元素
tm.put(new Student("pt546",21),"Beijing");
tm.put(new Student("pt03",23),"Shanghai");
tm.put(new Student("pt546",21),"Guangzhou");
tm.put(new Student("pt1",24),"Shenzhen");
tm.put(new Student("pt0001",22),"Tianjin");
//取出元素
Set<Map.Entry<Student,String>> entrySet = tm.entrySet();
Iterator<Map.Entry<Student,String>> it = entrySet.iterator();
while(it.hasNext())
{
Map.Entry<Student,String> me = it.next();
Student key = me.getKey();
String value = me.getValue();
System.out.println(key+"......"+value);
}
}
}
//建立一个比较器用于对年龄进行排序
class myComparator implements Comparator<Student>
{
public int compare(Student s1 , Student s2)
{
//先用compareTo比较年龄的值,并用int类型的变量记录住
int num = new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
//如果num一样,说明两个学生年龄一样,再按名字比较
if(num==0)
return s1.getName().compareTo(s2.getName());
//如果num!=0,返回num的值
return num;
}
}
运行结果
练习三
/*
Map练习三
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。
希望打印结果:a(1)c(2).....
看到希望打印结果这句,我就想到了映射,因为他们存在着映射关系,
每个字母都对应着它出现的次数。
所以这里采用Map集合
思路
1.将字符串转成字符数组
2.定义一个Map集合,因为打印的结果字母要有顺序,所以用TreeMap
3.遍历数组,将每一个字母都作为键用,,get方法去Map集合中查
如果返回null,就将该字母作为键和1作为值存入到Map集合中。
如果返回的不是null,就说明该字母已经作为键存在在Map集合中,并有对应的值
那么就获取该字母对应的值,并进行自增,然后将该字母和自增后的次数作为
一个键值对再存入集合中,覆盖原键对应的值
4.将Map中的元素按题意打印形式返回
虽然我用的是毕老师上课讲过的题,但我的代码都不是复制粘贴,是纯手打哦~
写个博客不容易啊!所有程序都手打,不过现在打代码速度快多了,熟能生巧 嘿嘿
*/
import java.util.*;
class MapTest3
{
public static void main(String[] args)
{
//要被计数的字符串
String s = "sd+++fgz0x c/v*asdfxcvdf";
//调用获取字母个数的方法,并输出结果
System.out.println(getCharCount(s));
}
//为了使主函数看起来不臃肿,单独封装获取字母个数方法
public static String getCharCount(String s)
{
//先将字符串变成字符数组
char[] arrc = s.toCharArray();
//定义一个TreeMep集合
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
//定义一个计数器
int count = 0;
//遍历数组
for(int i=0 ; i<arrc.length ; i++)
{
//先判断字符数组内的元素是否都是字母如果不是的话,则不参加计数
if(!(arrc[i]>='a' && arrc[i]<='z' || arrc[i]>='A' && arrc[i]<='Z'))
//如果是,程序继续
continue;
//通过getValue方法获取数组中元素对应的值,并用Integer类型变量接收
Integer value = tm.get(arrc[i]);
//如果value!=null,说明这个键已经存在
if(value!=null)
//用count将value的值记住
count = value;
//否则就是value==null,所以直接让count自增,上面的判断语句运行完也会执行自增
count++;
//将arrc[i]作为键,count作为值存入Map集合中,如果原来集合中没有arrc[i]键,
//就是新增加了一个键,如果有,就用新的值替代旧的值
//为什么count是int型的可以作为键存入Map集合?
//因为JDK1.5版本之后,自动装箱
tm.put(arrc[i],count);
}
//创建一个StringBuilder容器
//主要目的是可以让输出的结果符合题意
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 key = me.getKey();
Integer value = me.getValue();
//将键和值存入StringBuilder容器中
sb.append(key+"("+value+")");
}
//使用toString方法,将StringBuilder容器中的数组转换为字符串,并返回
return sb.toString();
}
}
运行结果
Map扩展知识
Map集合被使用是因为具备映射关系。在很多项目中,应用比较多的是一对多的映射关系,
这就可以通过嵌套的形式将多个映射定义到一个大的集合中,并将大的集合分级处理,形成一个体系。
如下面的程序所示
/*
map扩展知识。
map集合被使用是因为具备映射关系。
以下是班级对应学生,而学生中学号对应着姓名的映射关系:
"jichuban" "01" "pt1";
"jichuban" "02" "pt2";
"jiuyeban" "01" "pjz1";
"jiuyeban" "02" "pjz2";
就如同一个学校有多个教室。每一个教室都有名称。
*/
import java.util.*;
class MapExpand
{
public static void main(String[] args)
{
//基础班集合
HashMap<String,String> jiChuBan=new HashMap<String,String>();
//就业班集合
HashMap<String,String> jiuYeBan=new HashMap<String,String>();
//学校集合
HashMap<String,HashMap<String,String>> hM=new HashMap<String,HashMap<String,String>>();
//将班级名称作为键,不同班级学生作为值传入学校集合中。
hM.put("jichuban",jiChuBan);
hM.put("jiuyueban",jiuYeBan);
//将学号作为键,姓名作为值,传入基础班集合中
jiChuBan.put("01","pt1");
jiChuBan.put("02","pt2");
//将学号作为键,姓名作为值,传入就业班集合中
jiuYeBan.put("01","pjz1");
jiuYeBan.put("02","pjz2");
//直接输出所有学生的信息
getAllStudentInfo(hM);
}
//定义一个方法获取全部学生的信息,在哪个班级,名字,学号
public static void getAllStudentInfo(HashMap<String ,HashMap<String,String>> hM)
{
//迭代Map集合
for (Iterator<String> it=hM.keySet().iterator();it.hasNext() ; )
{
//用String类型变量接收班级名称
String s= it.next();
System.out.println(s+":");
//根据get方法获取班级的集合
HashMap<String,String> stu=hM.get(s);
//调用获取学生信息方法
getStudentInfo(stu);
}
}
//获取班级中学生的信息,包括姓名和学号
public static void getStudentInfo(HashMap<String,String> stu)
{
//遍历班级集合
for (Iterator<String> it=stu.keySet().iterator();it.hasNext() ; )
{
//获取学号,姓名
String key=it.next();
String value=stu.get(key);
System.out.println(key+"..."+value);
}
}
}
运行结果
那么现在问题来了,挖掘机技术。。。。。额不对,我们学习了这么多集合,那到底具体要用谁呢?
看一张我手画图就知道啦
按上图分析就好。果然按和毕老师说的一样,课学下来,画图用的还是不错的。
谢谢大家观看~
-----------android培训、java培训、java学习型技术博客、期待与您交流!---------