首页 > 代码库 > 12. 集合类Collection和Map
12. 集合类Collection和Map
<style>html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video { margin: 0; padding: 0; border: 0 } body { font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 1.6; color: #333; background-color: #fff; padding: 20px; max-width: 960px; margin: 0 auto } body>*:first-child { margin-top: 0 !important } body>*:last-child { margin-bottom: 0 !important } p,blockquote,ul,ol,dl,table,pre { margin: 15px 0 } h1,h2,h3,h4,h5,h6 { margin: 20px 0 10px; padding: 0; font-weight: bold } h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code { font-size: inherit } h1 { font-size: 28px; color: #000 } h2 { font-size: 24px; border-bottom: 1px solid #ccc; color: #000 } h3 { font-size: 18px } h4 { font-size: 16px } h5 { font-size: 14px } h6 { color: #777; font-size: 14px } body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child { margin-top: 0; padding-top: 0 } a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6 { margin-top: 0; padding-top: 0 } h1+p,h2+p,h3+p,h4+p,h5+p,h6+p { margin-top: 10px } a { color: #4183C4; text-decoration: none } a:hover { text-decoration: underline } ul,ol { padding-left: 30px } ul li>:first-child,ol li>:first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type { margin-top: 0px } ul ul,ul ol,ol ol,ol ul { margin-bottom: 0 } dl { padding: 0 } dl dt { font-size: 14px; font-weight: bold; font-style: italic; padding: 0; margin: 15px 0 5px } dl dt:first-child { padding: 0 } dl dt>:first-child { margin-top: 0px } dl dt>:last-child { margin-bottom: 0px } dl dd { margin: 0 0 15px; padding: 0 15px } dl dd>:first-child { margin-top: 0px } dl dd>:last-child { margin-bottom: 0px } pre,code,tt { font-size: 12px; font-family: Consolas, "Liberation Mono", Courier, monospace } code,tt { margin: 0 0px; padding: 0px 0px; white-space: nowrap; border: 1px solid #eaeaea; background-color: #f8f8f8 } pre>code { margin: 0; padding: 0; white-space: pre; border: none; background: transparent } pre { background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px } pre code,pre tt { background-color: transparent; border: none } kbd { background-color: #DDDDDD; background-image: linear-gradient(#F1F1F1, #DDDDDD); background-repeat: repeat-x; border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD; border-style: solid; border-width: 1px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; line-height: 10px; padding: 1px 4px } blockquote { border-left: 4px solid #DDD; padding: 0 15px; color: #777 } blockquote>:first-child { margin-top: 0px } blockquote>:last-child { margin-bottom: 0px } hr { clear: both; margin: 15px 0; height: 0px; overflow: hidden; border: none; background: transparent; border-bottom: 4px solid #ddd; padding: 0 } table th { font-weight: bold } table th,table td { border: 1px solid #ccc; padding: 6px 13px } table tr { border-top: 1px solid #ccc; background-color: #fff } table tr:nth-child(2n) { background-color: #f8f8f8 } img { max-width: 100% }</style>
1. Collection
1.1 什么是集合类
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的方式。
数组和集合类都是容器,他们有什么不同?
数组虽然也可以存储对象,但长度是固定的;集合的长度是可变的,数组中可以存储基本数据类型,集合只能存储对象。
集合类的特点:
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
子接口
Set,List
集合中只能放置对象的引用,不能放置原生数据类型,
我们需要使用原生数据类型的封装类才能加入到集合中
注意:每一个容器的存储方式都有不同,这个存储方式称之为:数据结构
1.2 Collection常用方法
add 添加
remove 移除
isEmpty 是否为空
iterator 迭代器遍历
size 集合中元素个数
1.3 什么是迭代器?
其实就是集合的取出元素的方式;我们把取出的方式定义在集合的内部,这样我们取出方式就可以直接访问集合内部的元素。
那么取出方式就被定义成了内部类;而每一个容器的数据结构不同,所以取出的动作细节也不一样,但是都有共性内容判断和取出。那么刻意将这些共性抽取。(这些内部类都符合规则,该规则是Iterator。通过对外提供的方法:iterator())
2. List
Collection中常见的两个接口List 和 Set
List:元素是有序的,元素可以重复,因为该元素有索引。凡是可以操作角标的方法都是该体系的特有方法。
实现类包括
LinkedList,Vector,ArrayList
列表接口,继承与Collection,可以按索引的顺序访问,有索引的Collection
具有列表的功能,元素顺序均是按添加的先后进行排列的,
允许重复的元素,允许多个null元素
增
add(index,element);
addAll(index,Collection)
删
remove(index);
改
set(index,element);
查
get(index);
subList(from,to);
ListIterator();
2.1 ArrayList
构造方法
public ArrayList()
public ArrayList(int initialCapacity)
public ArrayList(Collection c)
ArrayList依赖于数组实现的,初始长度为10的Object[],并且可随需要而增加的动态数组
当元素超过10,那么ArrayList底层会新生成一个数组,长度为原来的1.5倍+1,
然后将原数组内容复制到新数组中,并且后续增加的内容会放到新数组中,
当新数组无法容纳增加的元素,重复该过程
ArrayList对随机访问性能很好,但进行大量插入,删除操作,性能很差,
因为操作之后后续元素需要移动
ArrayList的操作示例:
class Demo{
public static void main(String[] args){
//创建一个集合容器,使用Collection接口的子类ArrayList
List list = new ArrayList();
//增
list.add("day01");
list.add("day02");
list.add("day03");
//在指定位置添加;
list.add(1,"day02");//相当于插入
//删除指定位置的元素
list.remove(2);
//修改
list.set(2,"改了");
//查(通过角标获取元素)
list.get(1)
//获取所有元素
for(int i = 0;i<list.size();i++){
System.out.print(list.get(i)+",")
}
System.out.println();
//迭代器获取所有元素
Iteractor it = list.iteractor();
while(it.hasNext()){
System.out.print(it.next()+",")
}
//通过indexof()获取对象的位置
list.indexof("day01");//返回是一个int值
List sub = list.subList(1,3);
System.out.println(sub);//包含1角标不包含3
}
}
列表迭代器:
List集合特有的迭代器,ListIteractor是Iteractor的子接口。
在迭代时,不可以通过集合对象的方法,操作集合中的元素,因为会发生集合并发异常,所以在迭代时,只能通过迭代器的方法操作元素。
可是Iteractor的方法只能对元素进行判断,取出,删除的操作。如果想要其他的操作如:添加,修改等,就需要使用其子接口,ListIteractor,该接口只能通过List集合的listIteractor方法获取。
class Demo{
public static void main(String[] args){
//创建一个集合容器,使用Collection接口的子类ArrayList
ArrayList list = new ArrayList();
//增
list.add("day01");
list.add("day02");
list.add("day03");
Iteractor it = list.iteractor();
// Iteractor it = list.iteractor();
// while(it.hasNext()){
// Object obj = it.next();
// if(obj.equals("day02")){
// //list.add("day007");异常,并发异常
// //只能使用迭代器的方法,如:
// it.remove();
// }
//
// }
ListIterator li = list.listIterator();
while(li.hasNext()){
Object obj = li.next();
if(obj.equals("day02")){
li.add("day007");
}
}
//反向从后往前面进行查找判断
while(li.hasPrevious()){
syso(li.previous());
}
}
}
总结:
底层的数据结构使用的是数组结构;特点:查询速度很快,但是增删就较慢(针对元素比较多的情况)
扩展:ArrayList的实现原理
2.2 LinkedList
LinkedList功能与ArrayList,Vector相同,内部是依赖双链表实现的,
因此有很好的插入和删除性能,但随机访问元素的性能很差
构造方法
public LinkedList()
public LinkedList(Collection c)
LinkedList类中有一个Entry内部类,Entry内部类包含3个部分向前的引用,向后的引用,数据
header.next = header.previous = header;
底层是链表数据结构。特点:增删掉速度很快,查询的时候较慢
代码示例:
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
LinkedList link = new LinkedList();
//方法:addFirst();从前面开始添加
link.addFirst("java01");
link.addFirst("java02");
link.addFirst("java03");
System.out.println(link);
// addlast();从后面开始添加元素
link.addLast("java04");
System.out.println(link);
// 获取第一个元素;如果没有元素会出现NoSuchElementException异常
System.out.println(link.getFirst());
// 获取最后一个元素
System.out.println(link.getLast());
// 获取第一个元素并删除;如果没有元素会出现NoSuchElementException异常
System.out.println(link.removeFirst());
System.out.println(link);
// 获取最后一个元素并删除
System.out.println(link.removeLast());
System.out.println(link);
// JDK1.6出现了替代方法,
// 从前面开始添加
link.offerFirst("java00");
System.out.println(link);
// 从后面开始添加
link.offerLast("java04");
System.out.println(link);
// 获取元素,但不删除元素,如果集合中没有元素,会返回null
link.peekFirst();
System.out.println(link);
link.peekLast();
System.out.println(link);
// 获取元素,但是元素被删除,如果集合中没有元素,会返回null
link.pollFirst();
System.out.println(link);
link.pollLast();
System.out.println(link);
}
}
扩展:LinkedList源码解析
2.3 vector
向量,历史比较悠久,Java诞生就有了,特点与ArrayList相同,
不同的是Vector操作元素的方法是同步的,同一时刻只能有一个线程访问,没有特殊需求都使用ArrayList
构造方法
public Vector()
public Vector(int initialCapacity)
public Vector(int initialCapacity,int capacityIncrement)
第一个参数是初始容量,第二个参数是当Vector满时的增量
public Vector(Collection c)
Vector也是依赖数组实现的
底层是数组数据结构。和ArrayList一样,区别:线程同步
代码示例:
import java.util.Enumeration;
import java.util.Vector;
public class VoctorDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Vector v = new Vector();
v.add("java01");
v.add("java02");
v.add("java03");
v.add("java04");
v.add("java05");
//枚举是Vector特有的取出方式
Enumeration en = v.elements();
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
// 枚举和迭代器很像,其实枚举和迭代是一样的,因为枚举的名称和方法都过长,所以被迭代器取代
}
}
扩展:Vector详细介绍(源码解析)和使用示例
2.4 List总结
2.5 练习
用ArrayList存储;
定义一个Person的类,存入ArrayList集合中,去除重复元素,定义:名字相同的和年龄都相同的为重复元素。
3. Set
实现类
HashSet,LinkedHashSet
子接口
SortSet
实现类
TreeSet
不包含重复元素,最多包含一个null,元素没有顺序
元素是无序(存入和取出的顺序不一定一致)的,元素不可以重复。
Set集合的功能和Collection是一致的。
它有两个常见的子类:hashSet、TreeSet
Set 集合常用方法:
first 返回 Set 当前第一个元素。
last 返回 Set 当前最后一个元素。
comparator 返回 Set 中元素进行比较的比较器,如果 Set 使用自然排序,则返回 null。
headSet 返回一个新集合,新集合包含参数对象前面的所有对象。
subSet 返回一个新集合,新集合包含参数对象之间的所有对象(前面的要取,后面的不取)。
tailSet 返回一个新集合,新集合包含参数对象以及其之后的所有对象。
3.1 HashSet
HashSet不是Ordered也不是Sorted,存储对象引用时是按照哈希策略来实现的,
HashSet中是否存在一个对象是通过equals()和hashCode()协同判断
不保证顺序
构造方法
public HashSet()
public HashSet(int initialCapacity)
public HashSet(Collection c)
HashSet底层是使用HashMap实现的
HashSet的add()方法详解:
判断已经存储在集合中的对象hashCode值是否与增加对象的hashCode值一致
如果不一致,直接加进去
如果一致,再进行equals()比较
如果equals()返回true,对象已经存在不增加进去
如果equals()返回false,把对象增加进去
数据结构是哈希表,线程是非同步的。
hashSet是通过里面的hashCode和equals方法来完成元素的唯一性,如果哈希值相同,才回判断equlas是否相同,如果哈希值不同,不会调用equlas方法。
HashSet判断和删除
对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法
练习:
练习hashSet里面方法;
定义一个Person的类,存入hashSet集合中,去除重复元素,定义:名字相同的和年龄都相同的为重复元素。
扩展:HashSet的源码分析
3.2 TreeSet
TreeSet是SortedSet接口的实现,元素不论以什么元素插入,在遍历的时候,都会以天然顺序遍历
TreeSet底层是使用TreeMap实现的
构造方法
public TreeSet()
public TreeSet(SortedSet s)
public TreeSet(int initialCapacity)
public TreeSet(Comparator<? super E>)
public TreeSet(Collection c)
因为TreeSet是带排序的,所以想要为TreeSet增加自定义类型,必须指定排序规则
可以对Set集合里面进行排序; 底层数据是:二叉树,保证元素唯一行的依据。CompareTo方法return 0。
代码示例:
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<String> tr = new TreeSet<String>();
tr.add("cba");
tr.add("abcd");
tr.add("aaa");
tr.add("bca");
tr.add("Aca");
Iterator<String> it = tr.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//输出:
// Aca
// aaa
// abcd
// bca
// cba
}
}
练习:
往TreeSet里面存储学生对象,按照学生的年龄进行排序,排序时,当主要条件相同时,一定要判断次要条件
排序的方式:让元素自身具备比较性,元素需要实现Comparable接口,覆盖compareTo方法。也称之为元素的自然顺序,或者也可以称之为默认顺序。
TreeSet集合的第二种比较方式
当元素自身不具备比较性时,或者具备的比较性不是所需要的,这时,集合需要具备比较性。
代码示例:
import java.util.Comparator;
public class MyCompare implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Student s1 = (Student)o1;
Student s2 = (Student)o2;
int num = s1.getName().compareTo(s2.getName());//先比较名字
if(num == 0){//如果名字相等
if(s1.getAge() > s2.getAge()){//比较年龄
return 1;
}else if(s1.getAge() == s2.getAge()){
return 0;
}
return -1;
}
return num;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Student> tr = new TreeSet<Student>(new MyCompare());
tr.add(new Student("zhangsan01",40));
tr.add(new Student("zhangsan02",21));
tr.add(new Student("zhangsan003",22));
Iterator<Student> it = tr.iterator();
while(it.hasNext()){
Student stu = it.next();
System.out.println(stu.getName()+"......"+stu.getAge());
}
}
}
两种比较器都存在时,我们都以比较器为准。
练习:
按照字符串长度排序,字符串本身具备比较性,但是它的比较方法不是我们所需要的,这时我们需要使用比较器。
扩展:TreeSet详细介绍(源码解析)和使用示例
4. Map
实现类
HashMap,LinkedHashMap,Hashtable
子接口
SortedMap
实现类
TreeMap
映射集,键值集合
Map增加对象时,如果key值在Map已经存在,将会替换原先value值
Map里面的key值是不能重复的,value值可以重复
Map的泛型不可以是基本数据类型,比如Map<int,int>报错
Map集合:该集合存储键值对,是一对一对的存储,注意:保证键值的唯一性。
Map集合常用方法:
put 向集合中添加指定的 key-value 映射关系。
containsKey 如果映射包含了参数 key,则返回 true。
containValue 如果有一个或者多个 key 映射到了参数 value,则返回 true。
get 获取 key 映射的值。
keySet 返回该集合中 所有 key 组成的 Set 集合,可使用 iterator() 遍历。
values 返回该集合中 所有 key 组成的 Set 集合,可使用 iterator() 遍历。
常用的三种Map类
Hashtable :底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。
HashMap :底层是哈希表数据结构,允许存入null键null值,该集合是不同步的
TreeMap :底层是二叉树数据结构,线程不同步。可以用于给map集合里面的键进行排序
4.1 HashMap
HashMap和ArrayList是一个时代的产物
既不是Ordered也不是Sorted,该类通过对键计算哈希码来决定值的存储,
不保证键的存储顺序,HashMap允许键值为null,但只能出现一次
构造方法
public HashMap()
初始容量为16
public HashMap(Map m)
常用方法示例:
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map map = new HashMap();
// 从此映射中移除所有映射关系
map.clear();
// 将指定的值与此映射中的指定键相关联
map.put("a", "lwc");
map.put("b", "nxj");
// 从指定映射中将所有映射关系复制到此映射中
map.putAll(new HashMap());
// 如果存在此键的映射关系,则将其从映射中移除
map.remove("b");
// 返回此映射中的键-值映射关系数
map.size();
// 如果此映射未包含键-值映射关系,则返回 true
map.isEmpty();
// 返回此映射中映射到指定键的值
map.get("a");
// 如果此映射包含指定键的映射关系,则返回 true
map.containsKey("a");
// 如果此映射为指定值映射一个或多个键,则返回 true
map.containsValue("nxj");
// 返回此映射中包含的映射关系的 set 视图
map.entrySet();
// 返回此映射中包含的键的 set 视图
map.keySet();
// 比较指定的对象与此映射是否相等
map.equals(new HashMap());
// 返回此映射的哈希码值
map.hashCode();
// 返回此映射中包含的值的 collection 视图
map.values();
}
}
遍历Map 获取Map值的方法:
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Map map = new HashMap();
map.put("a", "lwc");
map.put("b", "nxj");
// 方法一:
Set set = map.keySet();
Iterator ite1 = set.iterator();
while (ite1.hasNext()) {
String key = (String) ite1.next();
String value = http://www.mamicode.com/(String) map.get(key); ":" + value);
}
// 方法二:
Set<Map.Entry> set1 = map.entrySet();
Iterator<Map.Entry> ite2 = set1.iterator();
while (ite2.hasNext()) {
Map.Entry entry = (Map.Entry) ite2.next();
String key = (String) entry.getKey();
String value = http://www.mamicode.com/(String) entry.getValue(); ":" + value);
}
// 获取map中所有值对象的Collection集合
Collection collection = map.values();
for (Object c : collection) {
String value = http://www.mamicode.com/(String) c; "值为: " + value + ",长度为 " + value.length());
}
}
}
/*
打印结果:
b:nxj
a:lwc
b:nxj
a:lwc
值为: nxj,长度为 3
值为: lwc,长度为 3
*/
扩展:HashMap源码剖析
4.2 TreeMap
TreeMap是SortedMap接口的实现,键对象不论以什么顺序插入,在遍历的时候,都会以天然顺序遍历
构造方法
public TreeMap()
public TreeMap(SortedMap s)
public TreeMap(Map m)
public TreeMap(Comparator c)
c指定比较器,与TreeSet相同,如果想要指定键的排序顺序,可以使用此构造器
因为TreeMap是带排序的,所以想要为TreeMap增加自定义类型,必须指定排序规则
TreeMap排序规则Comparator案例
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
TreeMap map = new TreeMap(new PersonComparator());
map.put(new Person("lwc", 80), "1");
map.put(new Person("nxj", 70), "3");
map.put(new Person("lp", 60), "2");
map.put(new Person("fy", 75), "4");
Set set = map.keySet();
Iterator ite = set.iterator();
while (ite.hasNext()) {
Person key = (Person) ite.next();
String value = http://www.mamicode.com/(String) map.get(key); "编号:" + value + "\t姓名:" + key.name + "\t分数为:"
+ key.score);
}
}
}
class Person {
String name;
int score;
public Person(String name, int score) {
this.name = name;
this.score = score;
}
}
class PersonComparator implements Comparator {
public int compare(Object o1, Object o2) {
Person p1 = (Person) o1;
Person p2 = (Person) o2;
return p1.score - p2.score;
}
}
/*
打印结果:
编号:2 姓名:lp 分数为:60
编号:3 姓名:nxj 分数为:70
编号:4 姓名:fy 分数为:75
编号:1 姓名:lwc 分数为:80
*/
扩展:
TreeMap详细介绍(源码解析)和使用示例
Hashtable详细介绍(源码解析)和使用示例
Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)
练习:
每一个学生Student都有一个对应的归属地定义为String类型。学生属性:姓名,年龄
注意:姓名和年龄相同的视为同一个学生。保证学生的唯一性。
1、描述学生。
2、定义Map容器,将学生作为键,地址作为值存入集合中。
3、获取Map中的元素并进行排序。
12. 集合类Collection和Map