- Map:用于存储具备着对应关系的键和值。而且要保证键的唯一性。一次存储一对儿元素。
- Collection一次存储一个元素,称之为单列集合。
- Map集合一次存储一对儿元素,称之为双列集合。
Map常见方法:
1,添加。
v put(k,v):
void putAll(map);
2,删除。
void clear():清空集合。
v remove(key):通过键删除元素,并返回对应的值。
3,判断。
boolean containsKey(key);
boolean containsValue(value);
boolean isEmpty();
4,获取。
value get(key):根据键获取值。如果键不存在,返回null。可以用来判断一个集合中是否存在某一个键。
5,获取map中所有的元素(对应关系)。
Map是不具备迭代器的,获取原理就是将map集合先转成set集合。然后在使用迭代器。
Set<key> keySet();
Set<Map.Entry<key,value>> entrySet();
Colection<value> values();获取map集合中所有的值。
演示代码:
package com.itheima.collection;
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)
{
mapDemo();
}
private static void mapDemo()
{
Map<Integer, String> map = new HashMap<Integer, String>();
// 添加元素
map.put(24, "Kobe");
map.put(23, "Jordan");
map.put(3, "Paul");
map.put(16, "Paul");
System.out.println(map);
// 存了相同的键,会替换原来的值并返回
System.out.println(map.put(3, "Noname"));
System.out.println(map);
// 获取元素
System.out.println(map.get(16));
// 打印元素
printMap(map);
}
private static void printMap(Map<Integer, String> map)
{
// 通过Map集合的Set<K> keySet()方法实现
printMapByKeySet(map);
// 通过Map集合的Set<Map.Entry<K,V>> entrySet()方法实现
// Entry其实就是Map接口中的内部接口
prinMapByEntrySet(map);
// 通过values()方法获取map中所有的值,返回collection,因为值可以重复
prinMapByValues(map);
}
private static void prinMapByValues(Map<Integer, String> map)
{
Collection<String> coll = map.values();
for (Iterator<String> it = coll.iterator(); it.hasNext();)
{
String values = it.next();
System.out.println(values);
}
}
private static void prinMapByEntrySet(Map<Integer, String> map)
{
// 通过entrySet获取Map集合中的键值关系,存到set集合
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
// 通过set集合的迭代器遍历元素
for (Iterator<Map.Entry<Integer, String>> it = entrySet.iterator(); it
.hasNext();)
{
Map.Entry<Integer, String> me = it.next();
System.out.println(+me.getKey() + ":: " + me.getValue());
}
}
private static void printMapByKeySet(Map<Integer, String> map)
{
// 通过keySet获取map集合中的键集
Set<Integer> keySet = map.keySet();
// 通过迭代器取值
for (Iterator<Integer> it = keySet.iterator(); it.hasNext();)
{
Integer key = it.next();
String value = map.get(key);
System.out.println(key + ": " + value);
}
}
}
Map体系:
|--Hashtable:底层是哈希表数据结构,是线程同步的,不允许null作为键,null作为值。
|--HashMap:底层是哈希表数据结构,是线程不同步的,允许null作为键,null作为值。替代了Hashtable。
|--TreeMap:可以对Map集合中的键进行指定顺序的排序,默认是使用键的自然顺序。当然也可以使用比较器。
为了成功地在哈希表中存储和获取对象,用作键的对象必须实现
hashCode
方法和equals
方法。
需求:
将学生对象和归属地的映射关系存储到HashMap集合中,并全部取出。
学生对象中包含着姓名和年龄这样的属性。
公司:这里仅用字符串表示即可。
思路:对学生对象进行描述。同姓名同年龄视为同一人。
package com.itheima.bean;
public class Student
{
private String name;
private int age;
public Student()
{
super();
}
public Student(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 hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString()
{
return "name=" + name + ", age=" + age + ",Corp";
}
}
HashMap演示代码:
package com.itheima.collection;输出结果:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.itheima.bean.Student;
public class HashMapDemo
{
public static void main(String[] args)
{
HashMap<Student, String> hm = new HashMap<Student, String>();
hm.put(new Student("Lucy", 22), "Facebok");
hm.put(new Student("Mike", 23), "Mircosoft");
hm.put(new Student("Bob", 26), "Amzon");
hm.put(new Student("Tom", 23), "Google");
hm.put(new Student("Tom", 23), "Apple");
// 通过entrySet方法取出
Set<Map.Entry<Student, String>> entrySet = hm.entrySet();
for (Iterator<Map.Entry<Student, String>> it = entrySet.iterator(); it.hasNext();)
{
System.out.println(it.next());
}
}
}
name=Bob, age=26,Corp=Amzon
name=Lucy, age=22,Corp=Facebok
name=Mike, age=23,Corp=Mircosoft
name=Tom, age=23,Corp=Apple
对学生对象按照年龄排序
TreeMap演示代码:
package com.itheima.collection;输出结果:
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import com.itheima.bean.Student;
public class TreeMapDemo
{
public static void main(String[] args)
{
//TreeMap<Student, String> tm = new TreeMap<Student, String>();
// 自然顺序为按年龄排序,一旦定义就补可以更改
// 现在要按姓名排序,故使用比较器
TreeMap<Student, String> tm = new TreeMap<Student, String>(new Comparator<Student>()
{
public int compare(Student s1, Student s2)
{
int tmp = s1.getName().compareTo(s2.getName());
return tmp==0 ? s1.getAge() - s2.getAge() : tmp;
}
});
tm.put(new Student("Lucy", 22), "Facebok");
tm.put(new Student("Mike", 23), "Mircosoft");
tm.put(new Student("Bob", 26), "Amzon");
tm.put(new Student("Tom", 23), "Google");
tm.put(new Student("Tom", 23), "Apple");
// 通过entrySet方法取出
Set<Map.Entry<Student, String>> entrySet = tm.entrySet();
for (Iterator<Map.Entry<Student, String>> it = entrySet.iterator(); it.hasNext();)
{
System.out.println(it.next());
}
}
}
name=Bob, age=26,Corp=Amzon
name=Lucy, age=22,Corp=Facebok
name=Mike, age=23,Corp=Mircosoft
name=Tom, age=23,Corp=Apple
LinkedHashMap演示代码:
package com.itheima.collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapDemo
{
public static void main(String[] args)
{
HashMap<Integer, String> hm = new LinkedHashMap<Integer, String>();
hm.put(23, "Kobe");
hm.put(16, "Paul");
hm.put(3, "Paul");
hm.put(9, "paker");
printMethod_1(hm);
printMethod_2(hm);
printMethod_3(hm);
}
private static void printMethod_3(HashMap<Integer, String> hm)
{
for (Map.Entry<Integer, String> me : hm.entrySet())
{
Integer key = me.getKey();
String value = me.getValue();
System.out.println(key+"------"+value);
}
}
private static void printMethod_2(HashMap<Integer, String> hm)
{
for (Integer i : hm.keySet())
{
String value = hm.get(i);
System.out.println(i+"--"+value);
}
}
private static void printMethod_1(HashMap<Integer, String> hm)
{
for (Iterator<Map.Entry<Integer, String>> it = hm.entrySet().iterator(); it.hasNext();)
{
Map.Entry<Integer, String> me = it.next();
System.out.println(me.getKey()+"---"+me.getValue());
}
}
}
输入结果:
23---Kobe
16---Paul
3---Paul
9---paker
23--Kobe
16--Paul
3--Paul
9--paker
23------Kobe
16------Paul
3------Paul
9------paker
练习:
"abcdvbzvadsza"获取该字符串中每个字母出现的次数,
要求结果是: 字母(次数)字母(次数) 如:a(2)b(3)c(1)e(5)...
思路:
- 发现字母和次数之间存在对应关系。也就是映射关系,这就让我想到了map集合。
- map集合中应该存储是字母和次数,键是字母,值是次数。
- 将map作为一张表,使用了查表法思想。对字符串中的每一个字母都去map表中进行次数的查找。
- 如果没有找到对应的次数,就将该字母和1存储到表中。
- 如果找到了对应的次数,就将该次数取出,并对该次数自增后,在将该字母和新的次数重新存入表中,
- 对应map集合而言,键相同,值会覆盖
- 所有的字母都查询完毕后,map表就有了所有字母和次数的对应关系,
- 在将这些对应关系变成指定格式的字符串即可。
package com.itheima.collection;运行结果输出: a(3)b(2)c(1)d(2)s(1)v(2)z(2)
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapTest
{
public static void main(String[] args)
{
String str = "abcdvbzvadsza";
String strFin = getStrCount(str);
System.out.println(strFin);
}
private static String getStrCount(String str)
{
// 将字符串转换为字符数组
char[] chs = str.toCharArray();
// 定义TreeMap容器作为存储字母和次数的表
TreeMap<Character, Integer> map = new TreeMap<Character, Integer>();
// 遍历,查表,存值
for (int i = 0; i < chs.length; i++)
{
//判断是否是字母。
if(!(chs[i]>='a' && chs[i]<='z' || chs[i]>='A' && chs[i]<='Z'))
continue;
Integer value = map.get(chs[i]);
if (value==null) // 该字母不存在,存入键值1
value = 0;
value ++;//已经存在,加一
map.put(chs[i],value); // 替换原值
}
return mapToString(map);
}
private static String mapToString(TreeMap<Character, Integer> map)
{
StringBuilder sb = new StringBuilder();
/*for (Iterator<Map.Entry<Character, Integer>> it = map.entrySet().iterator(); it.hasNext();)
{
Map.Entry<Character, Integer> me = it.next();
Character key = me.getKey();
Integer value = me.getValue();
sb.append(key+"("+value+")");
}
*/
for (Map.Entry<Character, Integer> me : map.entrySet())
{
Character key = me.getKey();
Integer value = me.getValue();
sb.append(key+"("+value+")");
}
return sb.toString();
}
}
什么时候使用map呢?
- 当分析问题时,发现其中存在着映射关系,这时应该想到两个容器,一个是map,一个是数组。
- 如果映射关系的一方是有序的编号(可以作为索引),而且映射关系的个数确定,建议使用数组。否则,就使用map集合。
- 凡是映射关系的出现,都可以使用查表法的思想去解决问题。
其他集合什么时候使用呢?
- 如果要保证元素的唯一性,不存在映射关系,必须先考虑使用Set集合。
- 如果不需要保证唯一性,直接考虑List集合。
总结:List,Set,Map,数组的选择: 1,元素是否唯一? 不唯一,使用List.若需要查询快,选择ArrayList,需要增删快,选择LinkedList 唯一,选择Set或者Map 2,元素是否存在映射关系? 不存在,选择Set,不排序选择HashSet,其保证元素唯一性依赖hashCode(),equals()方法, 排序,选择TreeSet,自然排序,实现Comparable接口,覆盖 CompareTo()方法; 人为排序,使用比较器,实现Comparator接口,覆盖 Compare()方法 存在,如果映射关系的一方是有序的编号(可以作为索引),而且映射关系的个数确定,建议使用数组 其他使用Map
Collections使用方法举例说明:
package com.itheima.collection;输出结果:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class CollectionsDemo
{
public static void main(String[] args)
{
// List 中元素可以重复,但是无法排序,若需要排序,怎么办?
ArrayList<String> al = new ArrayList<String>();
al.add("hello");
al.add("aldga");
al.add("makie");
al.add("mskv");
al.add("hello");
System.out.println(al);
// 排序
sortDemo(al);
System.out.println("------------我是万恶的分割线---------------");
// 最值
maxDemo(al);
System.out.println("------------我是万恶的分割线---------------");
// 反转排序
reverseOrderDemo();
}
private static void reverseOrderDemo()
{
//TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder());
TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new Comparator<String>()
{
public int compare(String o1, String o2)
{
int tmp = o1.length() - o2.length();
return tmp==0 ? o1.compareTo(o2) : tmp;
}
}));
ts.add("agmlalg");
ts.add("sdgga");
ts.add("ahgdtge");
ts.add("adg");
ts.add("erqa");
System.out.println(ts);
}
private static void maxDemo(ArrayList<String> al)
{
// 求最值
String str = Collections.max(al);
System.out.println(str);
// 求最值,自己实现
String str3 = getMax(al);
System.out.println(str3);
// 按长度求最值
String str2 = maxByLen(al);
System.out.println(str2);
}
private static String maxByLen(ArrayList<String> al)
{
String str2 = Collections.max(al, new Comparator<String>()
{
public int compare(String o1, String o2)
{
int tmp = o1.length() - o2.length();
return tmp==0 ? o1.compareTo(o2) : tmp;
}
});
return str2;
}
private static String getMax(ArrayList<String> al)
{
Iterator<String> it = al.iterator();
String max = it.next();
while (it.hasNext())
{
String tmp = it.next();
if (tmp.compareTo(max) > 0)
max = tmp;
}
return max;
}
private static void sortDemo(ArrayList<String> al)
{
// 自然排序
Collections.sort(al);
System.out.println(al);
// 长度排序,比较器实现
Collections.sort(al, new Comparator<String>()
{
public int compare(String o1, String o2)
{
int tmp = o1.length() - o2.length();
return tmp==0 ? o1.compareTo(o2) : tmp;
}
});
System.out.println(al);
}
}
[hello, aldga, makie, mskv, hello]
[aldga, hello, hello, makie, mskv]
[mskv, aldga, hello, hello, makie]
------------我是万恶的分割线---------------
mskv
mskv
makie
------------我是万恶的分割线---------------
[ahgdtge, agmlalg, sdgga, erqa, adg]
目前我们学习的集合框架中的大部分常用的集合对象都是线程不同步的。
万一为多线程访问,会出现线程安全问题。
可以通过Collections中的同步集合方法,将非同步的集合,转换成同步的集合。
public static <T> Collection<T> synchronizedCollection(Collection<T> c)
public static <T> Set<T> synchronizedSet(Set<T> s)
public static <T> List<T> synchronizedList(List<T> lis
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
Arrays:操作数组的工具类。
- 里面都是静态方法。
- 重点介绍一个方法:数组转成集合。 asList([]);
- 数组对象能有的就是一个属性length,使用了Arrays以后,也就具备那么几个方法。
- 可是将数组转成集合后,就可以使用集合的方法来操作数组中的元素。 比如:contains方法。 indexOf
- 这样做了可以有更多的方法对数组中的元素进行操作。
- 但是有些方法不能用,因为数组的长度是固定的。不能使用增或删的方法。
package com.itheima.collection;输出结果:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArraysDemo
{
public static void main(String[] args)
{
int[] arr = {1, 3, 4, 56, 34, 3, 43};
System.out.println(Arrays.toString(arr));
String[] str = {"abc","haha","xixi"};
List<String> list = Arrays.asList(str);
//list.add("heihei");//UnsupportedOperationException
System.out.println(list.contains("haha"));
System.out.println(list);
//特殊情况。
Integer[] nums = {7,9,3};
List<Integer> list2 = Arrays.asList(nums);
System.out.println(list2.size());
// 集合转数组
// 集合转成数组的原因,限制对元素的操作,比如增删。
ArrayList<String> al = new ArrayList<String>();
al.add("agdla");
al.add("asdgae");
al.add("rehgre");
String arr2[] = al.toArray(new String[al.size()]);
System.out.println(Arrays.toString(arr2));
}
}
[1, 3, 4, 56, 34, 3, 43]
true
[abc, haha, xixi]
3
[agdla, asdgae, rehgre]
如果要传入多个参数,而且都是同一类型,可以定义数组类型的参数。
但是传递时,必须先有数组对象。
jdk1.5后,为了简化书写,出现了特性,可变参数。
同样还是代表数组,但是不需要在创建数组对象了,直接将数组中的元素作为参数传入即可。
它会自动的将这些参数封装到数组中。
局限性。可变参数必须定义在参数列表的最后面。
int sum = add5(4,1,7,4,8,9);
System.out.println(sum);
public static int add(int a,int... arr)
{
int sum = 0;
for(int x=0; x<arr.length; x++)
{
sum+=arr[x];
}
return sum;
}
如果要传入多个参数,而且都是同一类型,可以定义数组类型的参数。
但是传递时,必须先有数组对象。
jdk1.5后,为了简化书写,出现了特性,可变参数。
同样还是代表数组,但是不需要在创建数组对象了,直接将数组中的元素作为参数传入即可。
它会自动的将这些参数封装到数组中。
局限性。可变参数必须定义在参数列表的最后面。
int sum = add5(4,1,7,4,8,9);
System.out.println(sum);
public static int add(int a,int... arr)
{
int sum = 0;
for(int x=0; x<arr.length; x++)
{
sum+=arr[x];
}
return sum;
}
静态导入:
package com.itheima.collection
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
//将一个类中的静态成员进行导入。简化书写。
import static java.util.Collections.*;
import static java.lang.System.*;
public class StaticImportDemo
{
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc6");
list.add("abc2");
list.add("abc9");
sort(list);
max(list);
}
}