黑马程序员——Java基础---集合框架(二)及工具类Collections、Arrays

时间:2023-02-24 23:02:34

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

第一讲   Map集合

一、概述

1、简述  

Map<K,V>集合是一个接口,和List集合及Set集合不同的是,它是双列集合,并且可以给对象加上名字,即键(Key)

2、特点

1)该集合存储键值对。一对一对往里存。

2)而且要保证键的唯一性。

二、Map集合的子类

Map

|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。 

|--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。

|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。

三、Map集合的常用方法

1、添加

put(K key, V value);   //添加元素,如果出现添加时,相同的键。那么后添加的值会覆盖原有键对应值。并且put方法会返回被覆盖的值。

putAll(Map<? extends K,? extends V> m)   //添加一个集合

2、删除

clear();   //清空

remove(Object key);   //删除指定键值对

3、判断

containsValue(Object value) ;   //判断值是否存在

containsKey(Object key) ;   //判断键是否存在

isEmpty();   //判断是否为空

4、获取

get(Object key);   //通过键获取值

size();   //获取集合的长度

values();   //获取Map集合中的所有的值,返回一个Collection集合

Set<Map.Entry<K,V>> entrySet();

Set<K> keySet();

注:HashMap集合可以通过get()的返回值来判断一个键是否存在,通过返回null来判断。

四、Map集合的两种取出方式

Map集合的取出原理:将Map集合转成Set集合,再通过迭代器取出。

1、Set<K> keySet():将map中所有的键存入到Set集合。因为Set具备迭代器。所以可以用迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。

示例:

import java.util.*;

class MapDemo
{
public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();

map.put("02","tom2");
map.put("03","tom3");
map.put("01","tom1");
map.put("04","tom4");

//先获取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中的一个static内部接口。
示例:

import java.util.*;

class MapDemo2
{
public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();

map.put("02","tom2");
map.put("03","tom3");
map.put("01","tom1");
map.put("04","tom4");
//将Map集合中的映射关系取出,存入到Set集合中
Set<Map.Entry<String,String>> entrySet = map.entrySet();
//有了Set集合,就可以获取迭代器
Iterator<Map.Entry<String,String>> it = entrySet.iterator();

while(it.hasNext())
{
Map.Entry<String,String> me = it.next();
//通过Map.Entry中特有的方法getKey()和getValue()获取键和值
String key = me.getKey();
String value = me.getValue();

System.out.println(key+":"+value);
}
}
}
关于Map.Entry:

Map是一个接口。其实,Entry也是一个接口,Entry其实就是Map中的一个static内部接口。就相当于内部类一样。为什么要定义到内部呢?因为只有有了Map集合,有了键

值对,才会有键值的映射关系。关系属于Map集合中的一个内部事物。而且该事物在直接访问Map集合中的元素。

练习:

/*
需求:
每一个学生都有对应的归属地。
学生Student,地址String。
学生属性:姓名,年龄。
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性。

思路:
1、描述学生。
2、定义map容器。将学生作为键,地址作为值。存入。
3、获取map集合中的元素。
*/

import java.util.*;

//描述学生类
class Student implements Comparable<Student>
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
//复写compareTo
public int compareTo(Student s)
{
int num = new Integer(this.age).compareTo(new Integer(s.age));

if(num==0)
return this.name.compareTo(s.name);
return num;
}
//复写hashCode
public int hashCode()
{
return name.hashCode()+age*34;
}
//复写equals,比较对象内容
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name)&& this.age == s.age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
//复写toString(),自定义输出内容
public String toString()
{
return name+":"+age;
}
}


class MapTest
{
public static void main(String[] args)
{
Map<Student,String> map = new HashMap<Student,String>();

map.put(new Student("tom01",21),"beijing");
map.put(new Student("tom02",22),"shanghai");
map.put(new Student("tom03",23),"tianjin");
map.put(new Student("tom01",24),"lanzhou");

//第一种取出方式keySet

Set<Student> keySet = map.keySet();

Iterator<Student> it = keySet.iterator();

while(it.hasNext())
{
Student stu = it.next();
String addr = map.get(stu);
System.out.println(stu.getName()+":"+stu.getAge()+".."+addr);
}
//第二种取出方式 entrySet
Set<Map.Entry<Student,String>> entrySet = map.entrySet();

Iterator<Map.Entry<Student,String>> it1 = entrySet.iterator();

while(it1.hasNext())
{
Map.Entry<Student,String> me = it1.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu.getName()+":"+stu.getAge()+"...."+addr);
}

}
}
五、Map集合的扩展及应用

1、什么时候使用Map集合:当数据之间存在映射关系时,就应该首先想到用map集合。

2、示例:
获取某个字符串中的字母出现的次数。如:"sdfgzxcvasdfxcvdf",打印出:a(1)c(2).....的结果

通过结果发现,每一个字母都有对应的次数。说明字母和次数之间都有映射关系。

代码:

/*
思路:
1、将字符串转换成字符数组。因为要对每一个字母进行操作。

2、定义一个map集合,因为打印结果的字母有顺序,所以使用treemap集合。

3、遍历字符数组。
将每一个字母作为键去查map集合。
如果返回null,将该字母和1存入到map集合中。
如果返回不是null,说明该字母在map集合已经存在并有对应次数。
那么就获取该次数并进行自增。,然后将该字母和自增后的次数存入到map集合中。覆盖原来键所对应的值。

4、将map集合中的数据变成指定的字符串形式返回。
*/
import java.util.*;
class MapTest
{
public static void main(String[] args)
{
String s = charCount("absfafe,ferge+rtryhrjhy");
System.out.println(s);
}
//获取字符串中字符出现的次数
public static String charCount(String str)
{
char[] chs = str.toCharArray();
//定义一个TreeMap集合
TreeMap<Character,Integer> map = new TreeMap<Character,Integer>();

int count = 0;
for(int x=0;x<chs.length;x++)
{
if(!(chs[x]>='a' && chs[x]<='z') || chs[x]>='A' && chs[x]<='Z')
continue;//如果字符串中不是字母,则跳出本次循环进行下次循环

Integer value = map.get(chs[x]);

if(value!=null)//如果集合中没有该字母则存入
count = value;
count++;
map.put(chs[x],count);//存入键值对

count = 0;
}
//定义一个容器,将键值对存入
StringBuilder sb = new StringBuilder();
Set<Map.Entry<Character,Integer>> entrySet = map.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();
sb.append(ch+"("+value+")");
}
return sb.toString();//返回字符串
}
}
六、Map扩展知识

 在很多项目中,应用比较多的是一对多的映射关系,这就可以通过嵌套的形式将多个映射定义到一个大的集合中,并将大的集合分级处理,形成一个体系。

如:

/*
map扩展知识。
map集合被使用是因为具备映射关系。
下面是一个学校有多个教室。每一个教室都有名称。
*/

import java.util.*;

class Student
{
private String id;
private String name;
Student(String id,String name)
{
this.id = id;
this.name = name;
}
public String toString()
{
return id+":"+name;
}
}

class MapDemo3
{
public static void method_list()
{
//学校集合
HashMap<String,List<Student>> cxsw = new HashMap<String,List<Student>>();
//qh班集合
List<Student> qh = new ArrayList<Student>();
//zy班集合
List<Student> zy = new ArrayList<Student>();
//学校中班级集合和名称的集合
cxsw.put("siqi",qh);
cxsw.put("yiqi",zy);

//qh班中学生学号和姓名的映射
qh.add(new Student("01","tom"));
qh.add(new Student("02","Jerry"));

//zy班中学生学号和姓名的映射
zy.add(new Student("01","Danny"));
zy.add(new Student("02","Jack"));

Iterator<String> it = cxsw.keySet().iterator();

while(it.hasNext())
{
String className = it.next();
List<Student> clas = cxsw.get(className);

getInfos(clas);
}
}
//获取全部学生信息
public static void getInfos(List<Student> list)
{
Iterator<Student> it = list.iterator();
while(it.hasNext())
{
Student s = it.next();
System.out.println(s);
}
}
public static void main(String[] args)
{
method_list();

}
//获取班级中学生信息,包括姓名和学号
public static void getStudentInfo(HashMap<String,String> classMap)
{
Iterator<String> it = classMap.keySet().iterator();

while(it.hasNext())
{
String id = it.next();
String name = classMap.get(id);
System.out.println(id+":"+name);
}
}
}

第二讲   Collections
一、概述
Collections:集合框架的工具类。里面定义的都是静态方法。提供的方法中有可以对list集合进行排序,二分查找等方法。 通常常用的集合都是线程不安全的。因为要提高效率。

二、常见操作

1、查找

T max(Collection<? extends T> coll);//根据集合的自然顺序,获取coll集合中的最大元素

T max(Collection<? extends T> coll,Comparator<? super T> comp);//根据指定比较器comp的顺序,获取coll集合中的最大元素

int binarySearch(Lsit<? extends Comparable<? super T>> list,Tkey);//二分法搜索list集合中的指定对象

2、替换

void fill(List<? super T> list, T obj);//将list集合中的全部元素替换成指定对象obj

boolean replaceAll(List<T> lsit,T oldVal,T newVal);//用newVal替换集合中的oldVal值

void swap(Listlist,int i,int j);/在指定列表的指定位置处交换元素

3、排序

void shuffle(List<?> list);//使用默认随机源对list集合中的元素进行随机排序

void sort(List<T> list);//根据自然顺序对list集合中的元素进行排序

void sort(List<T> list,Comparator<? super T> c);//根据指定比较器c的排序方式对list集合进行排序

4、反转

reverse(List<?> list);//反转list集合中元素的顺序

Comparator reverseOrder();//返回一个比较器,强行逆转了实现Comparable接口的对象的自然顺序

Comparator reverseOrder(Comparator<T> cmp);//返回一个比较器,强行逆转了指定比较器的顺序

5、同步的集合

List<T>synchronizedList(List<T> list);//返回支持的同步(线程安全的)List集合

Map<K,V>synchronizedList(Map<K,V> m);//返回支持的同步(线程安全的)Map集合

三、Collections和Collectio的区别

Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。

它有两个常用的子接口:

List:对元素都有定义索引。有序的。可以重复元素。

Set:不可以重复元素。无序

Collections是集合框架中的一个工具类。该类中的方法都是静态的。提供的方法中有可以对list集合进行排序,二分查找等方法

通常常用的集合都是线程不安全的。因为要提高效率。如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。

练习:

/*
使用Collections中的fill对list集合中的部分元素替换成指定元素。
思路:
1、将List集合中要替换的部分元素取出,并存入另一个集合。
2、将原集合中要替换的元素移除。
3、用fill方法将要替换的元素进行替换。
4、将取出的部分增加进集合。
*/
import java.util.*;

class FillTest
{
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();

list.add("abc");
list.add("ab");
list.add("abcd");
list.add("a");
list.add("abcde");

try
{
fillSome(list,1,5,"heima");
}
catch (InputException e)
{
System.out.println(e.toString());
}
System.out.println(list);
}

//替换部分元素方法
public static void fillSome(List<String> list,int start,int end,String s)throws InputException
{
if(start>=end)
throw new InputException("没有要替换的元素");//如果输入的值小于或等于start,则抛出异常

//定义一个新集合
List<String> li = new ArrayList<String>();

//因为每移除一次,后面的元素就会补上,所以这里用y来控制次数
for (int x=start,y=start;y<end;y++)
{
li.add(list.get(x));//将需要替换的元素增加到新集合
list.remove(x);//移除需要替换的元素
}
Collections.fill(li,s);//替换成需要的元素s

list.addAll(start,li);//将替换的部分增加进原集合

}
}
//自定义异常
class InputException extends Exception
{
InputException(String message)
{
super(message);
}
}

第三讲   Arrays 一、概述 Arrays是用于操作数组的工具类。里边的方法也全是静态的把数组变成List集合的好处:可以使用集合的思想和方法来操作数组中的元素。如:contains,get,indexOf,subList等方法。

二、常见方法

1、Lsit<T> asList(T... a);//将数组转换为集合

注意:

a、将数组转换成集合,不可使用集合的增删方法,因为数组的长度是固定的。如果进行增删操作,则会产生UnsupportedOperationException的编译异常。

b、如果数组中的元素都是对象,则变成集合时,数组中的元素就直接转为集合中的元素。

c、如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。

2、binarySearch():二分查找方法,fill():替换方法,sort():排序方法等

特点:可对数组元素进行相应操作,可以接受除boolean之外的其他各种基本数据类型及有序的引用类型数组的参数,且还可以对指定元素的范围,并可根据指定比较器进

行相应的操作。

如:sort(T[]a,Comparator<? super T> c)

fill(int[]a,int from,int to)等

3、String toString();//可以接收各种数组类型参数,并返回指定数组内容的字符串表现形式。

示例:

import java.util.*;

class ArraysDemo
{
public static void main(String[] args)
{
//int[] arr = {2,3,4};
//System.out.println(Arrays.toString(arr));//转换为字符串形式

String[] arr = {"abc","dd","ffff"};

List<String> list = Arrays.asList(arr);
sop("contains:"+list.contains("dd"));//判断是否存在"dd"这个元素
//list.add("qq");

sop(list);

//int[] nums = {2,3,4};
Integer[] nums = {2,3,4};

List<Integer> list1 = Arrays.asList(nums);
sop(list1);
}
public static boolean myContains(String[] arr,String key)
{
for(int x=0;x<arr.length;x++)
{
if(arr[x].equals(key))
return true;
}
return false;
}
//打印方法
public static void sop(Object obj)
{
System.out.println(obj);
}
}
三、集合变数组

Collection接口中的toArray方法。

 <T> T[]toArray(T[] a);将集合变为指定类型的数组。

1、指定类型的数组到底要定义多长呢?

当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组。长度为集合的size。

当指定类型的数组长度大于了集合的size,就不会新创建了数组。而是使用传递进来的数组。

所以创建一个刚刚好的数组最优。

2、为什么要将集合变数组?

为了限定对元素的操作。不需要进行增删了。

示例:

import java.util.*;
class CollectionToArray
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<String>();
list.add("tom01");
list.add("tom02");
list.add("tom03");
//将集合变为String数组
String[] arr = list.toArray(new String[list.size()]);
sop(Arrays.toString(arr));//利用Arrays操作数组的方法
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}


第四讲   扩展知识---1.5版本新特性 一、高级for循环 1、格式 for(数据类型 变量名 : 被遍历的集合(Collection)或者数组){} 2、说明 a、对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。 b、迭代器除了遍历,还可以进行remove集合中元素的动作。如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。3、传统for和高级for的区别

高级for有一个局限性。必须有被遍历的目标(集合或数组)

传统for遍历数组时有索引。

建议在遍历数组的时候,还是希望是用传统for。因为传统for可以定义标。

示例:

import java.util.*;

class ForEachDemo
{
public static void main(String[] args)
{

//定义一个ArrayList集合
ArrayList<String> al = new ArrayList<String>();

al.add("abc1");
al.add("abc2");
al.add("abc3");


for(String s : al)
{
//s = "kk";
System.out.println(s);
}

System.out.println(al);

//传统for与高级for遍历数组
int[] arr = {3,5,1};

for(int x=0; x<arr.length; x++)
{
System.out.println(arr[x]);
}
for(int i : arr)
{
System.out.println("i:"+i);
}

//定义一个HashMap集合
HashMap<Integer,String> hm = new HashMap<Integer,String>();

hm.put(1,"a");
hm.put(2,"b");
hm.put(3,"c");
//keySet取出方式的高级for遍历
Set<Integer> keySet = hm.keySet();
for(Integer i : keySet)
{
System.out.println(i+"::"+hm.get(i));
}
//entrySet取出方式的高级for遍历
for(Map.Entry<Integer,String> me : hm.entrySet())
{
System.out.println(me.getKey()+"------"+me.getValue());
}
}
}


二、方法的可变参数

如果一个方法在参数列表中传入多个参数,个数不确定,那么每次要复写该方法。这时可以用数组作为形式参数,但是传入时,每次都要一个数组对象,作为实际参数。在

JDK1.5版本之后,就提供了一个新特性:可变参数。

可变参数其实就是上一种数组参数的简写形式。不用每一次都手动的建立数组对象。只要将要操作的元素作为参数传递即可。隐式将这些参数封装成了数组。

注意:在使用时,可变参数一定要定义在参数列表最后面。

示例:

class ParamMethodDemo 
{
public static void main(String[] args)
{
show("haha",2,3,4,5,6);
}
public static void show(String str,int...arr)
{
System.out.println(arr.length);
}

public static void show(int a,int b)
{
System.out.println(a+","+b);
}
}

三、静态导入

1、写法

import static  java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员。

import static  java.lang.System.*;//导入了System类中所有静态成员。

没加static导入的是类,加上static导入的是某一类中所有的静态成员。这样写在调用该类的静态方法时可以不用再写类名。如:Arra.sort(数组);就可以直接写sort(数组)。

2、注意

当导入的两个类中有同名成员时,需要在成员前加上相应的类名。

当类名重名时,需要指定具体的包名。当方法重名时,指定具备所属的对象或者类。

示例:

import static  java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员。

import static java.lang.System.*;//导入了System类中所有静态成员。

class StaticImport //extends Object 当没有指定继承时,所有类默认继承Object
{
public static void main(String[] args)
{

out.println("haha");//打印输出时可以直接省略数学System
int[] arr = {3,1,5};

sort(arr);//使用Arra工具类的方法sort时就可以省略书写Array
int index = binarySearch(arr,1);
//因为toString方法都具备,为了区分,必须写具体调用者
out.println(Arrays.toString(arr));
System.out.println("Index="+index);

}
}


------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------