集合框架
什么是集合框架呢?集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。
接口:即表示集合的抽象数据类型。接口提供了让我们对集合中所表示的内容进行单独操作的可能。
实现:也就是集合框架中接口的具体实现。实际它们就是那些可复用的数据结构。
算法:在一个实现了某个集合框架中的接口的对象身上完成某种有用的计算的方法,例如查找、排序等。
集合框架的构成图解:
根据图中的组成部分,我们开始讲解集合框架中的内容。
第一部分:Collection 和 Iterator
List :元素是有序的,元素可以重复
Set :元素是无序的,元素不可以重复。
|--List:元素是有序的,元素可以重复。因为该集合体系由索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快,增删稍慢。线程不同步,效率高
|--LinkedList:底层使用的是链表数据结构。特点:增删速度快,查询稍慢。
|--Vector:底层是数组数据结构。线程同步。效率低,被ArrayList替代了。
|--Set :元素是无序的,元素不可以重复。
|--HashSet:底层数据结构是 " 哈希表 "。
|--TreeSet:可以对 Set 集合中的元素进行判断。
集合框架的概念:
为什么会出现这么多的容器呢?因为每一个容器对数据的存储方式都有不同。这个存储方式称之为:数据结构。
一、ArrayList 和 迭代器Iterator
1.1 List 的重点部分:
List 集合特有的迭代器。ListIterator 是 Iterator 的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生修改异常。所以在迭代时,只能用迭代器的方法操作元素, 可是 Iterator 方法是有限的。只能对元素进行判断,取出,删除操作。如果想要其它的操作如添加,修改等,就需要使用其子接口 ListIterator
1.2 List:特有方法,凡是可以操作脚标的方法都是该体系的特有的方法。
1)增
add(index,element);
addAll(index,Collection)
2)删
remove(index);
3)改
set(index,elemnts)
4)查
get(index)
subList(from,to)
listIterator()
1.3 ArrayList 数组的默认长度:构造一个初始容量为10的数组。
1、集合中的 add 方法的参数类型是 Object 。以便于接受任意类型对象。
2、集合中存储的都是对象的引用(地址)。
1.4 什么是迭代器 Iterator?
迭代器是一种设计模式,通俗的讲就是:遍历集合,集合的取出元素的方式。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
1、使用next()获得序列中的下一个元素。
2、使用hasNext()检查序列中是否还有元素。
3、使用remove()将迭代器新返回的元素删除。
1.5 ArrayList 和 Iterator 的 示例:
package study.part1.day011.collection;
import java.util.*;
public class ListDemo {
public static void main(String[] args) {
ListMethod03();
}
public static void ListMethod01(){
ArrayList<String> al = new ArrayList<String>();
al.add("1234");
al.add("abcd");
al.add("ABCD");
al.add("XXOO12345!!@#$%");
System.out.println("原集合:"+al);
//在指定位置添加元素
al.add(2, "HELLO");
System.out.println("原集合:"+al);
//删除指定位置的元素
al.remove(2);
//修改元素
al.set(2, "XXX");
//通过脚标获取元素
al.get(3);
System.out.println("现集合:"+al);
//获取所有元素
for (int i = 0; i < al.size(); i++) {
System.out.println("al("+i+")"+al.get(i));
}
//迭代器的使用
Iterator<String> it = al.iterator();
while(it.hasNext()){
it.next();
System.out.println("next:"+it.next());
}
//通过indexOf获取对象的位置
al.indexOf("abcd");
List<String> sub = al.subList(1, 3);
System.out.println("sub"+sub);
}
/**
* 迭代器练习:在迭代过程中,准备添加或者删除元素
*/
public static void ListMethod02(){
ArrayList<String> al = new ArrayList<String>();
al.add("1234");
al.add("abcd");
al.add("ABCD");
al.add("XXOO12345!!@#$%");
System.out.println("原集合:"+al);
Iterator<String> it = al.iterator();
while(it.hasNext()){
Object obj = it.next();
if(obj.equals("abcd")){
al.add("HELLO");
al.remove(2);
}
System.out.println("原集合:"+al);
System.out.println("obj:"+obj);
}
}
/**
* 使用ListIterator
*/
public static void ListMethod03(){
ArrayList<String> al = new ArrayList<String>();
al.add("1234");
al.add("abcd");
al.add("ABCD");
al.add("XXOO12345!!@#$%");
System.out.println("集合:"+al);
ListIterator<String> li = al.listIterator();
while(li.hasNext()){
Object obj = li.next();
if(obj.equals("1234")){
li.add("XLPO");
}
}
System.out.println("集合:"+al);
System.out.println(""+li.hasNext());
System.out.println("aa:"+li.hasPrevious());
}
}
二、LinkedList
2.1 理解:底层使用的是链表数据结构。
特点:增删速度快,查询稍慢。
2.2 LinkedList 特有的方法:
1、addFirst();
2、addLast();
3、getFirst();
4、getLast();
获取元素,但是不删除元素。如果集合中没有元素,会出NoSuchElementException
5、removeFirst();
6、removeLast();
获取元素,但是元素被删除。如果果集合中没有元素,会出现NoSuchElementException
2.3 JDK 1.6 出现了替代方法
1、offerFirst()
2、offerLast()
3、peekFirst()
4、peekLast()
获取元素,但不删除元素,如果集合中没有元素,返回 null
5、pollFirst()
6、poolLast()
获取元素,但是元素被删除,如果集合中没有元素,返回 null
三、Vector
3.1 枚举:Enumeration就是 Vector 特有的取出方式。
3.2 发现枚举和迭代器很相向。其实枚举和迭代其实是一样的。因为枚举的名称以及方法的名称都过长,所以被迭代器取代了。
3.3 Demo :
package study.part1.day011.collection;
import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
vector01();
}
public static void vector01(){
Vector<String> v = new Vector<String>();
v.add("xxx");
v.add("ooo");
Enumeration<String> en = v.elements();
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
}
}
Set 分为 HashSet 和 TreeSet 两部分。
四、Set 中的 HashSet
1) HashSet:底层数据结构是 " 哈希表 "。哈希值如果一样,就对比是不是同一个对象。
2) HashSet 是如何保持元素的唯一性呢?
是通过元素的两个方法 hashCode 和 equals 来完成。
如果元素的 HashCode 值相同,才会判断 equals 是否为 true
如果元素的 HashCode 的值不同,就不会调用 equals 。
注意,对于判断元素是否存在,以及删除等操作。依赖的方法是元素的 hashCode 和 equals 方法。
五、Set 中的 TreeSet
1) TreeSet:可以对 Set 集合中的元素进行判断。
2) 注意:必须要让你的对象具备比较性,他才能进行比较排序,所以实现comparable这个比较接口就符合treeset要求了。这种比较性,称为底层数据结构是二叉树。它保证元素唯一性的依据:compareTo 方法 return 0;
3) TreeSet 排序的第一种方式:
让元素自身具有比较性。元素需要实现 comparable 接口。覆盖compareTo 方法。这种方式也成为元素的自然顺序,或者叫做默认顺序。
4) TreeSet 的第二种排序方式:
当元素自身比具备比较性,或具备的比较性不是所需的。这时,就需要让集合自身具备比较性。
5) 在集合一初始化时就有了比较方式。
6) 当元素自身比具备比较性,或具备的比较性不是所需的。这时,就需要让容器自身具备比较性。定义了比较器,将比较器对象作为参数传递给 TreeSet 集合的构造函数。
当两种排序都存在时,以比较器为主。
定义一个类,实现 Comparator 接口,覆盖 compare 方法。
实现 Comparator 的实例:
package study.part1.day011.collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet<Student2> ts = new TreeSet<Student2>(new MyCompare());
ts.add(new Student2("张L007",26));
ts.add(new Student2("张L0002",24));
ts.add(new Student2("张L01",22));
ts.add(new Student2("张L09",27));
Iterator<Student2> it = ts.iterator();
while(it.hasNext()){
Student2 stu = (Student2)it.next();
System.out.println(stu.getName()+"--"+stu.getAge());
}
}
}
//该接口强制让学生具备比较性。
class Student2 implements Comparable<Object>{
private String name;
private int age;
Student2(String name , int age){
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 obj) {
if(!(obj instanceof Student2)){
throw new RuntimeException("不是学生对象");
}
Student2 s = (Student2)obj;
System.out.println(this.name+"---compareto---"+s.name);
if(this.age>s.age){
return 1;
}
if(this.age==s.age){
return this.name.compareTo(s.name);
}
return -1;
}
}
class MyCompare implements Comparator<Object>{
@Override
public int compare(Object o1, Object o2) {
Student2 s1 = (Student2)o1;
Student2 s2 = (Student2)o2;
//return s1.getName().compareTo(s2.getName());
int num = s1.getName().compareTo(s2.getName());
if(num==0){
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
/**
if(s1.getAge()>s2.getAge()){
return 1;
}
if(s1.getAge()==s2.getAge()){
return 0;
}
return -1;
*/
}
return num;
}
}
7) TreeSet 练习:按照字符串长度排序
package study.part1.day011.collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetDemo3 {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<String>(new StrLenComparator());
ts.add("abc");
ts.add("cc");
ts.add("xxxx");
ts.add("jajwss");
Iterator<String> it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
class StrLenComparator implements Comparator<Object>{
@Override
public int compare(Object o1, Object o2) {
String s1 = (String)o1;
String s2 = (String)o2;
int num = new Integer(s1.length()).compareTo(s2.length());
if(num==0){
return s1.compareTo(s2);
}
return num;
}
}
第二部分:Map
一、 Map 基本内容
1)Map 集合的存储方式: 该集合存储键值对,成对性的往里存,而且要保证数据的唯一性。其实,Map 和 Set 很像,是因为 Set 的底层数据结构就是使用了 Map 集合。
注意了:当发现有存在映射关系时,可以选择 map 集合。因为 map 集合中存放的就是映射关系。
2)Map 集合中的三个子类:
|--HashTable:底层是哈希表数据结构,不可以存入 null 键和 null 值。该集合时线程同步的。 效率低。
|--HashMap: 底层是哈希表数据结构,允许使用 null 键和 null 值。该集合时不同步的。 效率高。
|--TreeMap: 底层是二叉树数据结构,线程不同步。可以用于给 Map 集合中的键进行排序。
3) Map 集合的基本操作:
1、添加。
put(K,V);
putAll(Map<? extends k,? extends v> m);
put 使用注意:两次添加元素,如果出现添加时相同的值,那么后添加的值会覆盖原有值键对应值。并且 put 方法会返回被覆盖的值。
2、删除。
clear();
remove(Object key);
3、判断。
containsValue(Object value);
containsKey(Object key);
isEmpty();
4、获取。
get(Object key);//可以通过 get 方法的返回值来判断一个键是否存在。
size();
values();//获取集合中的所有值
4) Map 集合中的重点:
entrySet();
keySet();
二、Map 集合中的两种取出方式。
1、Set<key> keySet:将 map 中所有的值存入到 Set 集合。因为 Set 具备迭代器。所以可以迭代方式取出所有的键,在根据 get 方法,获取每一个键的值。
Demo:
public class MapDemo2entrySet {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("zl02", "002");
map.put("zl03", "003");
map.put("zl04", "004");
//先获取 Map 集合中的所有键的 Set 集合,keySet()
Set<String> keySet = map.keySet();
//有了 Set 集合就可以获取迭代器。
Iterator<String> it = keySet.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
}
2、Set<Map.Entry<k,v>> entrySet:将 map 集合中的映射关系存入到 Set 集合中。而这个关系的数据类型就是:Map.Entry
Map.Entry:其实 Entry 也是一个接口,它是 Map 接口中的一个内部接口
Demo:
//接口中定义内部接口
interface MapSC{
public static interface Entry{
public abstract Object getKey();
public abstract Object getValue();
}
}
class HashMapSC implements MapSC{
class HashMapEnt implements MapSC.Entry{
@Override
public Object getKey() {
return null;
}
@Override
public Object getValue() {
return null;
}
}
}
3、Map 的练习
1) HashMap 练习:
需求:描述学生;定义 map 容器,将学生作为键,地址作为值,存入;获取 map 集合的元素。
Code 实现:
package study.part1.day011.collection;
import java.util.*;
public class MapDemoHashMapTest1 {
public static void main(String[] args) {
HashMap<StudentMT,String> hm = new HashMap<StudentMT,String>();
hm.put(new StudentMT("张三",12), "上海");
hm.put(new StudentMT("张L",13), "龙岩");
hm.put(new StudentMT("李四",16), "漳州");
hm.put(new StudentMT("王五",22), "龙海");
//第一种取出方式 Set
Set<StudentMT> keySet = hm.keySet();
//迭代器
Iterator<StudentMT> it = keySet.iterator();
while(it.hasNext()){
StudentMT stu = it.next();
String addr = hm.get(stu);
System.out.println(stu+":"+addr);
}
//第二种取出方式 entrySet
Set<Map.Entry<StudentMT, String>> entrySet = hm.entrySet();
//迭代器
Iterator<Map.Entry<StudentMT, String>> iter = entrySet.iterator();
while(it.hasNext()){
Map.Entry<StudentMT, String> me = iter.next();
StudentMT stu = me.getKey();
String addr = me.getValue();
System.out.println(stu+":"+addr);
}
}
}
class StudentMT implements Comparable<StudentMT>{
private String name;
private int age;
StudentMT(String name , int age){
this.name = name;
this.age = age;
}
//重写StudentMT的 toString 方法
@Override
public String toString() {
//return "StudentMT [name=" + name + ", age=" + age + "]";
return "姓名:"+name+"/年龄:"+age+"/地址";
}
//重写(复写)hashCode 方法
public int hashCode(){
return name.hashCode()+age*34;
}
//重写equals 方法
public boolean equals(Object obj){
if(!(obj instanceof StudentMT)){
//return false;
throw new ClassCastException("类型不匹配。");
}
StudentMT s = (StudentMT)obj;
return this.name.equals(s.name) && this.age==s.age;
}
//重写compareTo方法
public int compareTo(StudentMT s){
int num = new Integer(this.age).compareTo(new Integer(s.age));
if(num == 0){
return this.name.compareTo(s.name);
}
return num;
}
}
2) TreeMap 练习:
需求:"asdweiojdnnkgoss"获取该字符串中每一个字母出现的次数。希望打印结果:a(1),b(2),c(?)
通过分析发现,每一个字母都有对应的次数。说明字母和次数之间都有映射关系。
解题思路:
1、将字符串转成字符数组。因为要对每一个字母进行操作。
2、定义一个 map 集合,因为打印结果的字母有顺序,所以使用 TreeMap 集合。 3、遍历字符数组,将每一个字母作为键去查 map 集合。
如果返回 null ,将该字母和 1 存入到 Map 集合中。
如果返回不是 null ,说明该字母在 map 集合内已经存在。并有对应的次数。
那么久获取该次数并进行自增。然后将该字母和自增后的次数,存入到 map 集合中。覆盖掉原集合键所对应的值。
4、将 map 集合中的数据变成指定的字符串形式返回。
Code 实现:
package study.part1.day011.collection;
import java.util.*;
public class MapDemoTreeMapTest2 {
public static void main(String[] args) {
charCount("asdasdf12sadf");
}
public static String charCount(String str){
char[] chs = str.toCharArray();
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
for(int x = 0;x<chs.length;x++){
if(!(chs[x]>='a' && chs[x]<='z' || chs[x]>='A' && chs[x]<='Z')){
continue;
}
Integer value = tm.get(chs[x]);
if(value==null){
tm.put(chs[x], 1);
}else{
value = value+1;
tm.put(chs[x], value);
}
}
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 c = me.getKey();
Integer i = me.getValue();
sb.append(c+"{"+i+"}");
}
System.out.println(sb.toString());
return sb.toString();
}
}
第三部分:集合工具类 Collections 和 Arrays
一、Collections
1)常用方法:
1、sort 方法:给 list 集合排序,用 sort 方法。
2、max 方法:取 list 集合中的最大值。用 max 方法。
3、binarySearch:查询的字符串在集合中是否存在。返回 -1 ,表示不存在;返回 0 ,则存在。
2)使用方式:
Collections.sort(list);
二、Arrays
Arrays是用于操作数组的工具类。
1)常用方式
通过 asList 的方法,将数组变成 List 集合。
为什么把数组变成 List 集合的好处?
因为可以使用集合的思想和方法来操作数组中的元素。
Code 示例:
package study.part1.day012.collection;
import java.util.Arrays;
import java.util.*;
public class CollectionsDemo2 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
int [] arr = {1,2,3};
System.out.println(Arrays.toString(arr));
//总结:把数组变成 List 集合的好处?
//可以使用集合的思想和方法来操作数组中的元素。
String[] sarr = {"123","sadf","XXX"};
List<String> list = Arrays.asList(sarr);
System.out.println(list);
System.out.println("contains:"+list.contains("123"));
//整型数组变集合
Integer[] num = {1,2,3};
List<Integer> li = Arrays.asList(num);
}
}