Java中集合类的关系图
(虚线框是接口 实线框是类)
一、集合
集合:就是用于存储对象地址的。
集合的特点
1.用于存储对象的容器。
2.集合的长度是可变的。
3.集合中不可以存储基本数据类型值。
数组和集合的区别
数组和集合都可以存对象
数组是固定长度的,可以存储基本数据类型值
集合是可变长度的,不可以存储基本数据类型值
二、集合框架
集合容器因为内部的数据结构不同,有多种具体容器。
不断向上抽取,就形成了集合框架。
集合框架的顶层Collection接口
Collection的常见方法:
-
1.添加
添加一个:
boolean add(E e);
E其实就是object添加一些:
boolean addAll(Collection coll);
-
2.删除
删除一个:
boolean remove(Object o);删除一些:
boolean removeAll(Collection coll);删除全部(将集合中所有元素都删除)
void clear();
-
3.判断
判断collection是否包含指定的元素
boolean contains(object obj);判断collection是否包含指定的一些元素
boolean containsAll(Collection coll);判断集合是否为空
boolean isEmpty();
-
4.获取
获取长度
int size();获取集合元素——迭代器
Iterator iterator();
Iterator:迭代器对象
-
5.其他
取交集
boolean retainAll(Collection coll);将集合转成数组
Object[] toArray();
三、Iterator(迭代器)
迭代器的使用
取出集合中元素的方式
迭代器原理
该对象必须依赖于具体容器,因为每个容器的数据结构不同。所以该迭代器对象是在容器中进行内部实现的。
(迭代器是实现Iterator接口的每一个容器内部的内部对象)
对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器对象即可。也就是Iterator方法。
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
四、List(列表)
是Collection接口的子接口
特点
- 有序:存入和取出的顺序一致,元素都有索引(角标),元素可以重复。
特有的常见方法
List的共性特点就是都可以操作角标。
List元素可以完成对元素的增删改查!
-
添加
- 向列表尾部添加指定元素
boolean add(Object obj); - 在列表的指定位置插入指定元素
void add(index,element); - 添加指定 collection 中的所有元素到此列表的结尾
boolean addAll(Collection c); - 添加指定 collection 中的所有元素都插入到列表中的指定位置
boolean addAll(index,collection);
- 向列表尾部添加指定元素
-
删除
- 移除列表中指定位置的元素,返回那个被删的元素
Object remove(index);
- 移除列表中指定位置的元素,返回那个被删的元素
-
修改
- 用指定元素替换列表中指定位置的元素
Object set(index,element);
- 用指定元素替换列表中指定位置的元素
-
获取
- 获取列表中指定元素
Object get(index); - 获取此列表中第一次出现的指定元素的索引
int indexOf(object); - 获取此列表中最后出现的指定元素的索引
int lastIndexOf(object); - 获取子列表
fromIndex(包括 )和 toIndex(不包括)之间的元素
List subList(fromIndex,toIndex);
- 获取列表中指定元素
List特有获取元素方式:
ListIterator
使用迭代器,在迭代过程中,不能用集合的方式去添加元素(会出现异常),而迭代器本身又没有添加的方法,所以就有了ListIterator(列表迭代器)。
ListIterator是迭代器(Iterator)下的子接口
其中boolean hasNext();是正向遍历列表
boolean hasPreviours();是逆向遍历列表
List常用子类
Vector
内部是数组数据结构,
具有List的所有功能,
但是是同步的,也就是说是安全的
效率低
增删查询都很慢ArrayList(数组列表)
内部是数组数据结构,
是不同步的
效率高
替代了Vector
查询的速度快
举个栗子:
定义功能去除ArrayList中的重复元素
import java.util.ArrayList;
import java.util.Iterator;
/*
* 定义功能去除ArrayList中的重复元素
*
* */
public class HashSetDemo {
public static void main(String[] args){
ArrayList a1 = new ArrayList();
a1.add("abc1");
a1.add("abc2");
a1.add("abc2");
a1.add("abc1");
a1.add("abc");
System.out.println(a1);
a1 = getSingleElement(a1);
System.out.println(a1);
}
public static ArrayList getSingleElement(ArrayList a1) {
//1.定义一个临时容器
ArrayList temp = new ArrayList();
//2.迭代a1集合
Iterator it = a1.iterator();
while(it.hasNext())
{
Object obj = it.next();
//3.判断被迭代到的元素是否在临时容器中存在
if(!temp.contains(obj))
{
temp.add(obj);
}
}
return temp;
}
}
- LinkedList(链式列表)
内部是链表数据结构,
是不同步的
增删元素效率高
五、Set(集)
是Collection接口的子接口
特点
- 无序:元素不能重复。
set接口中的方法和collection的方法一样
Set常用子类
- HashSet(哈希表)
内部数据结构是哈希表
是不同步的
不保证迭代顺序
哈希是一种算法,算出来的值,存起来就是哈希表
自定义哈希算法
1.哈希表确定元素是否相同
a)判断的是两个元素的哈希值是否相同
如果相同,再判断两个对象的内容是否相同。
b)判断哈希值相同,其实判断的是对象的hashCode的方法。
判断内容相同,用的是equals方法
如果哈希值不同,就不需要判断equals。
- LinkedHashSet 类
具有可预知迭代顺序的set接口的哈希表和链接列表实现。
保证唯一且有序
- TreeSet
对Set集合中的元素进行排序。
元素唯一,判断方式就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。
不同步
1.TreeSet对元素进行排序的方式
- 让元素自身具备比较功能
元素需要实现comparable接口。覆盖compareTo方法。 - 让集合自身具备比较功能(比较器)
定义一个类Comparator接口,覆盖compare方法。
将该类对象作为参数传递给TreeSet集合的构造函数。
2.TreeSet——二叉树
按照二叉树原则比较
比较的是返回值
左小右大
不能重复
Comparable接口
对象之间要进行比较,就实现这个接口
强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
Comparator接口(比较器)
强行对某个对象 collection 进行整体排序 的比较函数。
方法
- 用来比较两个参数
int compare(Object arg0, Object arg1);
举个栗子:
以Person对象年龄(姓名)进行从小到大的排序
先建一个Person类
//要进行对象比较,所以Person类实现comparable接口。
public class Person implements Comparable {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Object o) {
Person p = (Person)o;
/* //按年龄比较
int temp = this.age-p.age;
return temp==0?this.name.compareTo(p.name):temp; */
//根据姓名比较
int temp = this.name.compareTo(p.name);
return temp==0?this.age-p.age:temp;
}
}
再建一个TreeSet类进行排序
import java.util.Iterator;
import java.util.TreeSet;
import admin.Person;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet t = new TreeSet();
t.add(new Person("zhangsan",22));
t.add(new Person("wangwu",28));
t.add(new Person("zhaosi",23));
t.add(new Person("wangyu",27));
Iterator it = t.iterator();
while(it.hasNext())
{
Person p = (Person)it.next();
System.out.println(p.getName()+"年龄:"+p.getAge());
}
}
}
上述方法用的是Person类中本身具备的比较方法
再用比较器对其进行排序
构建一个根据姓名比较的比较器类
import java.util.Comparator;
import admin.Person;
public class ComparatorByName implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
Person p1 = (Person)arg0;//强转
Person p2 = (Person)arg1;
int temp = p1.getName().compareTo(p2.getName());
return temp==0?p1.getAge()-p2.getAge():temp;
}
}
再将这个比较器链到比较的类中
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet t = new TreeSet(new ComparatorByName());
t.add(new Person("zhangsan",22));
t.add(new Person("wangwu",28));
t.add(new Person("zhaosi",23));
t.add(new Person("wangyu",27));
Iterator it = t.iterator();
while(it.hasNext())
{
Person p = (Person)it.next();
System.out.println(p.getName()+"年龄:"+p.getAge());
}
}
}
当两种情况都存在,按照比较器排序。
比较器更为常用
再吃一个小栗子
对字符串进行长度排序
import java.util.Iterator;
import java.util.TreeSet;
import Comparator.ComparatorBylength;
/*
* 对字符串进行长度排序
*
* 思路
* 1.TreeSet本身具有自然排序
* 2.但需要的是字符串长度排序
* 3.建一个构造器!
* */
public class TreeSetPractise {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new ComparatorBylength());
ts.add("aaaa");
ts.add("asg");
ts.add("df");
ts.add("gtty");
ts.add("akdjahds");
Iterator it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
构建一个根据字符串长度比较的比较器类
import java.util.Comparator;
public class ComparatorBylength implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String s1 = (String)o1;
String s2 = (String)o2;
int temp = s1.length()-s2.length();
return temp==0?s1.compareTo(s2):temp;
//compareTo是字符串自身具备的方法
}
}
六、Map集合
1.Map集合和Collection集合的区别
Map集合:一次添加一对元素;Map集合也称为双列集合。
Collection集合:一次添加一个元素;Collection集合也称为单列集合
2.Map<K,V>接口
K:key,键
V:value,值
也就是说Map集合中存储的是键值对。
Map中必须保证键的唯一性。
一个映射不能包含重复的键;每个键最多只能映射到一个值。
映射就是给一个对象(可以是变量、物体、等等)起一个唯一的别名。
3.常见方法
添加
value put(key,value);
返回前一个和key关联的值,如果没有返回null。-
删除
- 清空Map集合。
void clear(); - 根据指定的key删除这个键值对。
value remove(key);
- 清空Map集合。
-
判断
- 判断是否包含键。
boolean containsKey(key); - 判断是否包含值。
boolean containsValue(value); - 判断是否有键值对。
boolean isEmpty();
- 判断是否包含键。
-
获取
- 通过键获取值。如果没有该键返回null。
value get(key);
因此可以通过返回null,判断是否包含指定的键。 - 获取键值对的个数。
int size();
- 通过键获取值。如果没有该键返回null。
演示:
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap();
method(map);
}
public static void method(Map<Integer,String> map){
//<Integer,String>泛型(学号,姓名)
//添加
System.out.println(map.put(8, "wangcai"));
System.out.println(map.put(8, "xiaofang"));
//存相同键,值会覆盖。
map.put(2, "lele");
map.put(5, "feifei");
System.out.println(map);//打印集合
//删除
System.out.println("remove:"+map.remove(2));
System.out.println(map);//打印集合
//格式:键=值
//判断
System.out.println("containskey:"+map.containsKey(7));
System.out.println(map);//打印集合
//获取
System.out.println("get:"+map.get(8));
System.out.println("get:"+map.get(6));//不存在返回值为null
System.out.println(map);//打印集合
}
}
运行结果:
上述获取元素只能获取一个,但想要获取所有元素,就要用到keySet();
Set keySet()
返回此映射中包含的键的 Set 视图。
原理:
通过keySet方法获取map中所有的键所在的Set集合,再通过Set的迭代器获取。
再对每一个键通过map集合的get方法获取其对应的值即可。
举个栗子:
代码:
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<Integer,String> map = new HashMap();
method_2(map);
}
public static void method_2(Map<Integer,String> map){
map.put(2, "lulu");
map.put(3, "lele");
map.put(4, "feifei");
map.put(6, "yuanyuan");
map.put(9, "baibai");
//取出map中的所有元素。
//原理:通过keySet方法获取map中所有的键所在的Set集合,再通过Set的迭代器获取
//再对每一个键通过map集合的get方法获取其对应的值即可。
Set<Integer> keySet = map.keySet();
Iterator<Integer> it = keySet.iterator();
while(it.hasNext()){
Integer key = it.next();//获取到所有的键
String value = map.get(key);//获取键对应的值
System.out.println(key+":"+value);
}
}
}
运行结果:
画个图更好的理解keySet()
Set entrySet()
返回此映射中包含的映射关系的 Set 视图。
原理:通过Map转成set就可以迭代
该方法将键和值的映射关系作为对象存储到了Set集合中
根据Iterator迭代器通过Map.Entry对象的getKey,getValue获取其中的键和值。
这个映射关系的类型就是Map.Entry类型
代码:
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<Integer,String> map = new HashMap();
// method(map);
// method_keySet(map);
method_entrySet(map);
}
public static void method_entrySet(Map<Integer,String> map){
map.put(2, "lulu");
map.put(3, "lele");
map.put(4, "feifei");
map.put(6, "yuanyuan");
map.put(9, "baibai");
/*原理:通过Map转成set就可以迭代
该方法将键和值的映射关系作为对象存储到了Set集合中
这个映射关系的类型就是Map.Entry类型*/
Set<Map.Entry<Integer,String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer,String>> it = entrySet.iterator();
while(it.hasNext()){
Map.Entry<Integer,String> ma = it.next();
Integer key = ma.getKey();
String value = ma.getValue();
System.out.println(key+":::"+value);
}
}
}
运行结果:
Collection<V> values();
返回此映射中包含的值的 Collection 视图。
只获取value的值不要key
代码:
import java.util.Collection;
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<Integer,String> map = new HashMap();
method_values(map);
}
public static void method_values(Map<Integer,String> map){
map.put(2, "lulu");
map.put(3, "lele");
map.put(4, "feifei");
map.put(6, "yuanyuan");
map.put(9, "baibai");
//只获取value的值
Collection<String> values = map.values();
Iterator<String> it = values.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
运行结果:
3.Map常用的子类
-
Hashtable
内部结构是哈希表
是同步的
不允许null作为键,null作为值- Properties类
这个类表示了一个持久的属性集。
用来存储键值对型的配置文件信息,可以和IO技术相结合。
- Properties类
HashMap
内部结构是哈希表
不同步
允许null作为键,null作为值
HashMap存储自定义对象
举个栗子:
将学生对象和学生的归属地通过键与值存储到map集合中。
HashMapDemo代码如下:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import admin.Student;
public class HashMapDemo {
public static void main(String[] args) {
HashMap<Student,String> hm = new HashMap<Student,String>();
hm.put(new Student("lisi",21),"北京");
hm.put(new Student("wangwu",34),"上海");
hm.put(new Student("zhangsan",19),"大连");
hm.put(new Student("zhaoliu",25),"西安");
hm.put(new Student("lisi",21),"沈阳");
/* Set<Student> keySet = hm.keySet();
Iterator<Student> it = keySet.iterator(); */
//上面两句等价于下面这句
Iterator<Student> it = hm.keySet().iterator();
while(it.hasNext()){
Student key = it.next();
String value = hm.get(key);
System.out.println(key.getName()+":"+key.getAge()+"----"+value);
}
}
}
Studen类代码:
public class Student extends Person {
public Student() {
super();
}
public Student(String name, int age) {
super(name, age);
}
@Override
public String toString(){
return "student:"+getName()+":"+getAge();
}
}
运行结果:
- TreeMap
内部结构是二叉树
不同步
可以对Map集合中的键进行排序
TreeMap存储自定义对象
举个栗子:
TreeMapDemo类代码:
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import admin.Student;
import comparator.ComparatorByName;
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<Student,String> tm = new TreeMap<Student,String>(new ComparatorByName());
tm.put(new Student("lisi",21),"北京");
tm.put(new Student("wangwu",34),"上海");
tm.put(new Student("zhangsan",19),"大连");
tm.put(new Student("zhaoliu",25),"西安");
tm.put(new Student("lisi",21),"沈阳");
/*Set<Student> keySet = tm.keySet();
Iterator<Student> it = keySet.iterator();*/
Iterator<Map.Entry<Student,String>> it = tm.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Student, String> me = it.next();
Student key = me.getKey();
String value = me.getValue();
System.out.println(key.getName()+":"+key.getAge()+"----"+value);
}
}
}
ComparatorByName类比较器代码:
import java.util.Comparator;
import admin.Person;
public class ComparatorByName implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
Person p1 = (Person)arg0;//强转
Person p2 = (Person)arg1;
int temp = p1.getName().compareTo(p2.getName());
return temp==0?p1.getAge()-p2.getAge():temp;
}
}
运行结果(按姓名排过序的):
- LinkedHashMap
由于HashMap是按照Hash自己的排序方式存放的有序,但是当想取出来的时候就会不好找,用LinkedHashMap存放,好取好找。
举个栗子:
举一个Map集合的大栗子
获取字符串中,每个字母的出现次数
"fdgadhajskhslkha"
要求打印结果为:a(2)b(1)……
代码如下:
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/*
* 获取字符串中,每个字母的出现次数
*"fdgadhajskhslkha"
*要求打印结果为:a(2)b(1)……
*
*思路:
*1.由打印结果得出字母和次数之间存在这映射关系。
*2.这种映射关系很多,所以需要存储
*3.能存储映射关系的容器有数组和Map集合
*4.数组有编号,集合没有
*5.打印结果关系不需要有序编号
*6.使用Map集合
*7.但打印字母有排序
*8.可以使用TreeMap集合
*9.集合中应该存储的是字母和次数的对应关系
*10.将字符串的字母变成字符数组
*11.遍历字符数组,用字母作为键去查Map集合这个表
*12.如果该字母键不存在,就将这个字母作为键1作为值存储在Map集合中
* 如果该字母键存在,就将字母的键对应值取出并+1,再将该字母和+1后的值存储到map集合中
* 键相同值会覆盖,这样就记录了改字母的次数
*13.遍历结束,map集合就记录了所有字母出现的次数
*/
public class MapPractice {
public static void main(String[] args) {
String str = "fdgadhajskhslkha";
String s = getCharCount(str);
System.out.println(s);
}
//获取字符个数
public static String getCharCount(String str) {
//将字符串变成字符串数组
char[] cha = str.toCharArray();
//定义map集合表
Map<Character,Integer> map = new TreeMap<Character,Integer>();
for (int i = 0; i < cha.length; i++) {
//将数组中的字母作为键去查map表
Integer value = map.get(cha[i]);
//判断值是否为null
int count = 0;
if(value!=null){
count = value;
}
count++;
map.put(cha[i], count);
/*if(value==null){
map.put(cha[i], 1);
}else{
map.put(cha[i], value+1);
}*/
}
return mapToString(map);
}
private static String mapToString(Map<Character, Integer> map) {
StringBuilder sb = new StringBuilder();
Iterator<Character> it = map.keySet().iterator();
while(it.hasNext()){
Character key = it.next();
Integer value = map.get(key);
sb.append(key+"("+value+")");
}
return sb.toString();
}
}
运行结果:
七、集合查阅技巧
使用集合的判断技巧:
1.判断是否需要唯一:
需要:set ——>转2
不需要:List ——>转3
2.判断是否需要制定顺序:
需要:TreeSet
不需要:HashSet
需要一个和存储一致的顺序(有序):LinkedHashSet
3.判断是否需要频繁增删:
需要:LinkedList
不需要:ArrayList
记录容器结构和所属体系
1.List
ArrayList
LinkedList
2.Set
HashSet
TreeSet
后缀名就是该集合所属体系。
前缀名就是该集合的数据结构。
array:就能想到是数组,而且查询快,有角标。
link:就能想到链表,也就是说增删快。
hash:就能想到哈希表,还有唯一性,元素需要覆盖hashcode方法和equal方法。
Tree:就能想到二叉树,也就说能排序,就能想到有两个接口 Comparable,Comparator
而且常用的集合都是不同步的。