Map
Map与Collection的不同
- Map与Collection在集合框架中属并列存在
- Map存储的是键值对
- Map存储元素使用put方法,Collection使用add方法
- Map集合没有直接取出元素的方法,而是先转成Set集合,在通过迭代获取元素
Map集合中键要保证唯一性
总结:Map是一个双列集合,一次存一对(键值对),而且要保证键的唯一性。
Map集合中的常用方法
Map集合中的常用方法有:
- 添加
V put(K key, V value)
void putAll(Map<? extends K,? extends V> m)
- 删除
void clear()
rV remove(Object key)
- 判断
boolean containsKey(Object key)
boolean containsValue(Object value)
boolean isEmpty()
- 获取
V get(Object key)
int size()
Collection<V> values()
entrySet()
keySet()
下面我们就来演示以上的一些方法。首先我们创建一个集合容器,使用Map接口的一个实现类——HashMap。
Map<String, String> map = new HashMap<String, String>();
添加元素
map.put("01", "zhangsan1"); map.put("02", "zhangsan2"); map.put("03", "zhangsan3");
注意:添加元素时,如果出现相同的键,那么后添加的值会覆盖原有的键对应的值,并且put()方法会返回被覆盖的值。如:
System.out.println("put:"+map.put("01", "zhangsan1")); // null,因为"01"键所对应的原来的值为null System.out.println("put:"+map.put("01", "wangwu")); // zhangsan1,因为此时"01"键所对应的原来的值为"zhangsan1"
判断
System.out.println("containsKey:"+map.containsKey("022")); // containsKey:false
删除
System.out.println("remove:"+map.remove("02")); // remove:zhangsan2
获取
System.out.println("get:"+map.get("023")); // get:null
在通过get()方法获取集合中的元素时,可以发现:
- 可以通过get()方法的返回值来判断一个键是否存在,通过返回null来判断。
HashMap
允许使用null
值和null
键,如:map.put("04", null); System.out.println("get:"+map.get("04")); // get:null map.put(null, "haha"); System.out.println("get:"+map.get(null)); // get:haha
获取集合中所有的值
Collection<String> coll = map.values(); System.out.println(coll);
Map集合中常用类
Map
- HashTable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。JDK1.0,效率低。
- HashMap:底层是哈希表数据结构,允许使用null值和null键,该集合是不同步的。JDK1.2,效率高。
- LinkedHashMap:HashMap接口的子类,该接口是哈希表和链接列表的实现,具有可预知的迭代顺序。在后面的学习中会经常用到。
- TreeMap:底层是二叉树数据结构,线程不同步,可以给Map集合中的键进行排序。排序原理与TreeSet相同。
Map和Set很像,其实Set底层就是使用了Map集合。
Map集合的两种取出方式
Map集合的两种取出方式:
Set<K> keySet()
:将map所有的键存入到Set集合,因为Set集合具备迭代器,所以可以通过迭代方式取出所有的键,再根据get()方法,获取每一个键对应的值。Map集合的取出原理:将map集合转成set集合,再通过迭代器取出。Set<Map.Entry<K,V>> entrySet()
:将map集合中的映射关系存入到了Set集合中,而这个关系的数据类型就是:Map.Entry。
现在我们就来演示Map集合的第一种取出方式。
import java.util.*;
class MapDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("02", "zhangsan2");
map.put("03", "zhangsan3");
map.put("01", "zhangsan1");
map.put("04", "zhangsan4");
// 先获取map集合的所有的键的Set集合,keySet()
Set<String> ketSet = map.keySet();
// 有了Set集合,就可以获取其迭代器
Iterator<String> it = ketSet.iterator();
while(it.hasNext()) {
String key = it.next();
// 有了键就可以通过map集合的get()方法获取其对应的值
String value = map.get(key);
System.out.println("key:"+key+", value:"+value);
}
}
}
紧接着我们再来演示Map集合的第二种取出方式。
import java.util.*;
class MapDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("02", "zhangsan2");
map.put("03", "zhangsan3");
map.put("01", "zhangsan1");
map.put("04", "zhangsan4");
// 将map集合中的映射关系取出,存入到set集合中
Set<Map.Entry<String, String>> entrySet = map.entrySet();
Iterator<Map.Entry<String, String>> it = entrySet.iterator();
while(it.hasNext()) {
Map.Entry<String, String> me = it.next();
String key = me.getKey();
String value = me.getValue();
System.out.println(key+":"+value);
}
}
}
Map.Entry,其实Entry也是一个接口,它是Map接口中的一个内部接口。源码我们可以理解为:
interface Map {
// entry就是Map接口中的内部接口
public static interface Entry {
public abstract Object getKey();
public abstract Object getValue();
}
}
class HashMap implements Map {
class HaHa implements Map.Entry {
public Object getKey() {}
public Object getValue() {}
}
}
练习一、每一个雇员都有对应的归属地。雇员Employee,地址String。雇员属性:姓名,年龄。将雇员和归属地存储到HashMap集合中并取出,注意:姓名和年龄相同的视为同一个雇员,须保证雇员的唯一性。
解:
描述雇员,由于要将雇员对象作为键存入HashMap集合中,所以为了保证键的唯一性,雇员类中应该覆盖掉hashCode()和equals()方法。
public class Employee { private String name; private int age; public Employee() { super(); } public Employee(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 String toString() { return "Employee [name=" + name + ", 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; Employee other = (Employee) 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; } }
定义Map容器,将雇员作为键,地址作为值存入。
Map<Employee, String> map = new HashMap<Employee, String>(); map.put(new Employee("xiaozhang", 24), "北京"); map.put(new Employee("laoli", 34), "上海"); map.put(new Employee("mingming", 26), "南京"); map.put(new Employee("xili", 30), "广州"); map.put(new Employee("laoli", 34), "铁岭");
获取map集合中的元素,在这里我们只使用keySet()方法进行取出。
Set<Employee> keySet = map.keySet(); for (Employee employee : keySet) { String value = map.get(employee); System.out.println(employee.getName() + ":" + employee.getAge() + "..." + value); }
练习二、现在我们又有了这样一个需求:按照雇员的年龄进行升序排序并取出。
解:因为数据是以键值对的形式存在的。所以要使用可以排序的Map集合——TreeMap。
描述雇员,为了能按照雇员的年龄进行升序排序,我们可以让自定义的Employee类实现Comparable接口(强制让Employee类具备比较性),覆盖compareTo()方法。
public class Employee implements Comparable<Employee> { private String name; private int age; public Employee() { super(); } public Employee(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 String toString() { return "Employee [name=" + name + ", 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; Employee other = (Employee) 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 int compareTo(Employee o) { int temp = this.age - o.age; return temp == 0 ? this.name.compareTo(o.name) : temp; } }
定义Map容器,将雇员作为键,地址作为值存入。
Map<Employee, String> map = new TreeMap<Employee, String>(); map.put(new Employee("xiaozhang", 24), "北京"); map.put(new Employee("laoli", 34), "上海"); map.put(new Employee("mingming", 26), "南京"); map.put(new Employee("xili", 30), "广州"); map.put(new Employee("laoli", 34), "铁岭");
获取map集合中的元素,在这里我们只使用entrySet()方法进行取出。
Set<Map.Entry<Employee, String>> entrySet = map.entrySet(); for (Map.Entry<Employee, String> me : entrySet) { Employee key = me.getKey(); String value = me.getValue(); System.out.println(key.getName() + ":" + key.getAge() + "..." + value); }
练习三、现在我们又有了这样一个需求:按照雇员的姓名进行升序排序并取出。
解:因为数据是以键值对的形式存在的。所以要使用可以排序的Map集合——TreeMap。
- 描述雇员,雇员类在以上练习中我们已描述,在此省略。
定义一个按姓名排序的比较器。
Comparator<Employee> comparator = new Comparator<Employee>() { @Override public int compare(Employee o1, Employee o2) { int temp = o1.getName().compareTo(o2.getName()); return temp == 0 ? o1.getAge() - o2.getAge() : temp; } };
定义Map容器,将雇员作为键,地址作为值存入。
Map<Employee, String> map = new TreeMap<Employee, String>(comparator); map.put(new Employee("xiaozhang", 24), "北京"); map.put(new Employee("laoli", 34), "上海"); map.put(new Employee("mingming", 26), "南京"); map.put(new Employee("xili", 30), "广州"); map.put(new Employee("laoli", 34), "铁岭");
获取map集合中的元素,在这里我们只使用entrySet()方法进行取出。
Set<Map.Entry<Employee, String>> entrySet = map.entrySet(); for (Map.Entry<Employee, String> me : entrySet) { Employee key = me.getKey(); String value = me.getValue(); System.out.println(key.getName() + ":" + key.getAge() + "..." + value); }
练习四、获取字符串(比如”bwaerbctyxbacecrtdcvr”)中每一个字母出现的次数,要求结果格式为:a(2)b(1)d(3)…
解:通过结果发现,每一个字母都有对应的次数,说明字母和次数之间具有映射关系。注意:当发现有映射关系时,可以选择map集合,因为map集合中存放的就是映射关系。
本题思路:
- 将字符串转换为字符数组。因为要对每一个字母进行操作。
- 定义一个Map集合,因为打印结果的字母有顺序,所以使用TreeMap集合。
- 遍历字符数组,将每一个字母作为键去查map集合。如果返回null,将该字母和1存入到map集合中。如果返回不是null,说明该字母在map集合中已经存在,并有对应次数。那么就获取该次数并进行自增,然后将该字母和自增后的次数存入到map集合中,覆盖掉原来键所对应的值。
- 将map集合中的数据变成指定的字符串返回。
public class Test {
public static void main(String[] args) {
/*
* 作业:"bwaerbctyxbacecrtdcvr"
* 获取字符串中每一个字母出现的次数,要求结果格式:a(2)b(1)d(3)...
*/
String str = "bw?aer+bct=yxb-acecrtdcvr";
String char_count = getCharCount(str);
System.out.println(char_count);
}
public static String getCharCount(String str) {
// 1,将字符串转成字符数组
char[] chs = str.toCharArray();
// 2,定义Map集合表
Map<Character, Integer> map = new TreeMap<Character, Integer>();
// 3,遍历字符数组,获取每一个字母
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]);
int count = 0; // 用于记录次数
// 如果次数存在,就用count记录该次数。如果次数不存在,就不记录,只对count自增变成1
if (value != null) {
count = value;
}
count++;
map.put(chs[i], count);
/*
if (value == null) {
map.put(chs[i], 1);
} else {
value++;
map.put(chs[i], value);
}
*/
}
return toString(map);
}
/*
* 将Map集合中的元素转成指定格式的字符串。a(2)b(1)d(3)...
*/
private static String toString(Map<Character, Integer> map) {
// 1,数据多,无论类型是什么,最终都要变成字符串,所以可以使用StringBuilder
StringBuilder sb = new StringBuilder();
// 2,遍历集合map。keySet
Set<Character> keySet = map.keySet();
for (Iterator<Character> it = keySet.iterator(); it.hasNext();) {
Character key = it.next();
Integer value = map.get(key);
// 将键值存储到sb中
sb.append(key + "(" + value + ")");
}
return sb.toString();
}
}
结论:
问:什么时候使用Map集合呢?
答:当需求中出现映射关系时,应该最先想到Map集合。
map集合扩展知识——一对多关系
一对多的关系:一个学校有多个教室,每一个教室都有名称。一个教室有多个学生。假设学生属性:学号和姓名。
学生未封装成对象时
import java.util.*; class MapDemo { public static void main(String[] args) { // 首先定义一个学校 HashMap<String, HashMap<String, String>> czbk = new HashMap<String, HashMap<String, String>>(); // 接着再定义两个教室,预热班和就业班 HashMap<String, String> yure = new HashMap<String, String>(); HashMap<String, String> jiuye = new HashMap<String, String>(); // 学校和教室建立对应关系 czbk.put("yureban", yure); czbk.put("jiuyeban", jiuye); // 教室里面装有学生 yure.put("01", "zhangsan"); yure.put("02", "lisi"); jiuye.put("01", "zhaoliu"); jiuye.put("02", "wangwu"); // 遍历czbk集合,获取所有的教室 Iterator<String> it = czbk.keySet().iterator(); while(it.hasNext()) { String roomName = it.next(); HashMap<String, String> room = czbk.get(roomName); System.out.println(roomName); getStudentInfo(room); } } public static void getStudentInfo(HashMap<String, String> roomMap) { Iterator<String> it = roomMap.keySet().iterator(); while(it.hasNext()) { String id = it.next(); String name = roomMap.get(id); System.out.println(id+"::"+name); } } }
学生封装成对象,这个是经常用到的。将学生封装成对象:
class Student { private String id; private String name; public Student(String id, String name) { this.id = id; this.name = name; } public String toString() { return id + ":::" + name; } }
class MapDemo { public static void demo() { // 首先定义一个学校 HashMap<String, List<Student>> czbk = new HashMap<String, List<Student>>(); // 接着再定义两个教室,预热班和就业班,注意此时使用的是List集合 List<Student> yure = new ArrayList<Student>(); List<Student> jiuye = new ArrayList<Student>(); // 学校和教室建立对应关系 czbk.put("yureban", yure); czbk.put("jiuyeban", jiuye); // 教室里面装有学生 yure.add(new Student("01", "zhangsan")); yure.add(new Student("04", "wangwu")); jiuye.add(new Student("01", "zhouqi")); jiuye.add(new Student("02", "zhaoliu")); Iterator<String> it = czbk.keySet().iterator(); while(it.hasNext()) { String roomName = it.next(); List<Student> room = czbk.get(roomName); System.out.println(roomName); getInfos(room); } } 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) { demo(); } }
集合框架中的工具类
Collections
Collections类中定义的都是操作Collection的静态方法。
Collections常见方法
sort()
public static <T extends Comparable<? super T>> void sort(List<T> list)
:根据元素的自然顺序对指定列表按升序进行排序,列表中的所有元素都必须实现Comparable接口。public static void methodDemo1() { List<String> list = new ArrayList<String>(); list.add("abce"); list.add("z"); list.add("hehe"); list.add("nba"); System.out.println(list); // 对list排序,自然排序使用的是元素的compareTo方法 Collections.sort(list); System.out.println(list); }
以上是使用Collections类中的sort()方法对List集合进行排序,而且使用的是自然排序。但现在我们想要按照字符串的长度进行排序,那又该如何做呢?这时需要用到下面的方法。
public static <T> void sort(List<T> list, Comparator<? super T> c)
:根据指定比较器产生的顺序对指定列表进行排序。此列表内的所有元素都必须可使用指定比较器相互比较。
很显然,这时我们需要自定义一个比较器,使得比较是按照字符串的长度来排序的。public class ComparatorByLength implements Comparator<String> { @Override public int compare(String o1, String o2) { int temp = o1.length() - o2.length(); return temp == 0 ? o1.compareTo(o2) : temp; } }
测试方法为:
public static void methodDemo1() { List<String> list = new ArrayList<String>(); list.add("abce"); list.add("z"); list.add("hehe"); list.add("nba"); System.out.println(list); // 想按照长度排序 Collections.sort(list, new ComparatorByLength()); System.out.println(list); }
max()
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
:根据元素的自然顺序,返回给定Collection的最大元素。Collection中的所有元素都必须实现Comparable接口。
示例代码如下:public static void maxDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); list.add("qq"); list.add("z"); Collections.sort(list); // 可以看出元素的自然顺序结果 System.out.println(list); String max = Collections.max(list); System.out.println("max="+max); }
但现在要让我们自己模拟一个获取集合最大值的功能,那又该怎么做呢?直接贴出代码如下:
/** * 模拟一个获取集合最大值的功能 */ public static <T extends Object & Comparable<? super T>> T getMax(Collection<? extends T> coll) { Iterator<? extends T> it = coll.iterator(); // 1,定义变量记录容器中的其中一个。 T max = it.next(); // 2,遍历容器所有的元素 while (it.hasNext()) { T temp = it.next(); // 3,在遍历的过程中进行比较,只要比变量中的值大,就用变量记录下来。 if (temp.compareTo(max) > 0) { max = temp; } } return max; }
我们查看Java源代码,发现它写的和我们写的一模一样。
public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
:根据指定比较器产生的顺序,返回给定Collection的最大元素。Collection中的所有元素都必须可通过指定比较器相互比较。
首先,我们先自定义一个比较器——ComparatorByLength.java。public class ComparatorByLength implements Comparator<String> { @Override public int compare(String o1, String o2) { int temp = o1.length() - o2.length(); return temp == 0 ? o1.compareTo(o2) : temp; } }
我们的测试代码为:
public class CollectionsDemo { public static void main(String[] args) { Collection<String> coll = new ArrayList<String>(); coll.add("abcd"); coll.add("aa"); coll.add("z"); coll.add("nba"); String max = Collections.max(coll, new ComparatorByLength()); System.out.println("max = " + max); } }
binarySearch()
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
:使用二分搜索法搜索指定列表,以获得指定对象。在进行此调用之前,必须根据列表元素的自然顺序对列表进行升序排序(通过sort(List)方法)。如果搜索键包含在列表中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
示例代码如下:public static void binarySearchDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); list.add("qq"); list.add("z"); Collections.sort(list); System.out.println(list); int index = Collections.binarySearch(list, "aaa"); System.out.println("index="+index); // index=0 }
其内部原理是:
public static int halfSearch(List<String> list, String key) { int max, min, mid; max = list.size() - 1; min = 0; while(min <= max) { mid = (max + min) >> 1; // /2 String str = list.get(mid); int num = str.compareTo(key); if(num > 0) max = mid - 1; else if(num < 0) min = mid + 1; else return mid; } return -min-1; }
现在我们调用
halfSearch
方法:public static void binarySearchDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); list.add("qq"); list.add("z"); Collections.sort(list); System.out.println(list); int index = halfSearch(list, "aaaa"); System.out.println("index="+index); // index=-2 }
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
:使用二分搜索法搜索指定列表,以获得指定对象。在进行此调用之前,必须根据指定的比较器对列表进行升序排序(通过sort(List, Comparator) 方法)。
首先,我们先自定义一个比较器——ComparatorByLength.java。public class ComparatorByLength implements Comparator<String> { @Override public int compare(String o1, String o2) { int temp = o1.length() - o2.length(); return temp == 0 ? o1.compareTo(o2) : temp; } }
然后编写我们的测试方法:
public static void binarySearchDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); list.add("qq"); list.add("z"); Collections.sort(list, new StrLenComparator()); System.out.println(list); int index = Collections.binarySearch(list, "aaa", new StrLenComparator()); System.out.println("index="+index); // index=3 }
其内部原理是:
public static int halfSearch(List<String> list, String key, Comparator<String> cmp) { int max, min, mid; max = list.size() - 1; min = 0; while(min <= max) { mid = (max + min) >> 1; // /2 String str = list.get(mid); int num = cmp.compare(str, key); if(num > 0) max = mid - 1; else if(num < 0) min = mid + 1; else return mid; } return -min-1; }
现在来测试我们编写的方法,调用
halfSearch
方法的代码:public static void binarySearchDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); list.add("qq"); list.add("z"); Collections.sort(list, new ComparatorByLength()); System.out.println(list); int index = halfSearch(list, "cc", new ComparatorByLength()); System.out.println("index="+index); // index=-2 }
public static <T> void fill(List<? super T> list, T obj)
:使用指定元素替换指定列表中的所有元素。
示例代码如下:public static void fillDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); System.out.println(list); Collections.fill(list,"pp"); System.out.println(list); }
练习、复写fill(),将list集合中部分元素替换成指定元素。
public static void fillDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); System.out.println(list); fill(list, 1, 3, "hello"); System.out.println(list); } public static void fill(List<String> list, int start, int end, String str) { for(int i = start; i < end; i++) { list.set(i, str); } }
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
:使用另一个值替换列表中出现的所有某一指定值。
示例代码如下:public static void replaceAllDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); System.out.println(list); Collections.replaceAll(list, "aaa", "pp"); System.out.println(list); }
public static void reverse(List<?> list)
:反转指定列表中元素的顺序。
示例代码如下:public static void replaceAllDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); System.out.println(list); Collections.reverse(list); System.out.println(list); }
reverseOrder()
public static <T> Comparator<T> reverseOrder()
:返回一个比较器,它强行逆转实现了Comparable接口的对象Collection的自然顺序。
我们首先按照字符串的自然顺序排序:public static void orderDemo() { TreeSet<String> ts = new TreeSet<String>(); ts.add("abcde"); ts.add("aaa"); ts.add("k"); ts.add("cc"); Iterator it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next()); } }
接下来,我们要反转其自然排序:
public static void orderDemo() { TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder()); ts.add("abcde"); ts.add("aaa"); ts.add("k"); ts.add("cc"); Iterator it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next()); } }
当然了,我们可以使用比较器来做。首先我们定义一个反转字符串的自然顺序的比较器:
public class StrComparator implements Comparator<String> { public int compare(String s1, String s2) { // 这样写太繁琐 /* int num = s1.compareTo(s2); if(num > 0) return -1; if(num < 0) return 1; return num; */ return s2.compareTo(s1); // 简写 } }
然后我们调用此比较器:
public static void orderDemo() { TreeSet<String> ts = new TreeSet<String>(new StrComparator()); ts.add("abcde"); ts.add("aaa"); ts.add("k"); ts.add("cc"); Iterator it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next()); } }
public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
:返回一个比较器,它强行逆转指定比较器的顺序。
我们首先定义一个按照字符串的长度排序的比较器:public class ComparatorByLength implements Comparator<String> { @Override public int compare(String o1, String o2) { int temp = o1.length() - o2.length(); return temp == 0 ? o1.compareTo(o2) : temp; } }
接着,我们按照字符串的长度排序:
public static void orderDemo() { TreeSet<String> ts = new TreeSet<String>(new ComparatorByLength()); ts.add("abcde"); ts.add("aaa"); ts.add("k"); ts.add("cc"); Iterator it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next()); } }
最后,我们反转其顺序:
public static void orderDemo() { TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new StrLenComparator())); ts.add("abcde"); ts.add("aaa"); ts.add("k"); ts.add("cc"); Iterator it = ts.iterator(); while(it.hasNext()) { System.out.println(it.next()); } }
public static void shuffle(List<?> list)
:使用默认随机源对指定列表进行置换。所有置换发生的可能性都是大致相等的。
示例代码为:public static void shuffleDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); list.add("qq"); list.add("z"); System.out.println(list); Collections.shuffle(list); System.out.println(list); }
swap(List<?> list, int i, int j)
:在指定列表的指定位置处交换元素。(如果指定位置相同,则调用此方法不会更改列表。)
示例代码:public static void sortDemo() { List<String> list = new ArrayList<String>(); list.add("abcd"); list.add("aaa"); list.add("zz"); list.add("kkkkk"); list.add("qq"); list.add("z"); System.out.println(list); Collections.swap(list, 1, 2); System.out.println(list); }
- Collections还有一个可以将非同步集合转成同步集合的方法。
- 同步集合 synchronized集合(非同步的集合)
Arrays
Arrays:用于操作数组的工具类。里面都是静态方法。
Arrays的2个常见方法
toString()
:返回指定数组内容的字符串表示形式。int[] arr = {2, 4, 5}; System.out.println(Arrays.toString(arr));
public static <T> List<T> asList(T... a)
:返回一个受指定数组支持的固定大小的列表。String[] strs = {"abc", "haha", "nba", "zz"}; List<String> list = Arrays.asList(strs); // list.add("qq"); // 会发生java.lang.UnsupportedOperationException,注意不能使用集合的增删方法,即不能改变长度。 System.out.println(list.contains("nba"));
那么问题就来了,把数组变成List集合有什么好处?
答:可以使用集合的思想和方法来操作数组中的元素。但要注意:将数组变成集合,不可以使用集合的增删方法。因为数组的长度是固定的。可以使用集合的诸如contains()
、get()
、indexOf()
、subList()
等等方法,如果你增删,那么会生成UnsupportedOperationException
。再看下面一段代码:
int[] nums = {2, 4, 5}; List li = Arrays.asList(nums); System.out.println(li);
此时将会输出
[[I@139a55]
类似结果,以上代码没有使用泛型,使用泛型应是如此:int[] nums = {2, 4, 5}; List<int[]> li = Arrays.asList(nums); System.out.println(li);
如果代码修改为:
Integer[] nums = {2,4,5}; List<Integer> li = Arrays.asList(nums); System.out.println(li);
此时输出
[2, 4, 5]
。所以,得出结论:如果数组中的元素都是引用数据类型,那么变成集合时,数组中的元素就直接转成集合中的元素。如果数组中的元素都是基本数据类型,那么会将该数组([I@139a55)对象作为集合中的元素存在。
集合变数组:Collections接口中的toArray()方法
例,Collections接口中的toArray()方法的应用。
import java.util.*;
class CollectionToArray {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc1");
al.add("abc2");
al.add("abc3");
String[] arr = al.toArray(new String[al.size()]);
System.out.println(Arrays.toString(arr));
}
}
- 指定类型的数组到底要定义多长呢?
当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size。当指定类型的数组长度大于了集合的size,那么就不会新创建数组,而是使用传递进来的数组。所以创建一个刚刚好的数组最优。 - 为什么要将集合变数组?
为了限定对元素的操作,不需要进行增删了。
JDK1.5新特性
高级for循环
高级for循环的作用:用于遍历Collection集合或数组。
格式:
for(数据类型(一般是泛型类型) 变量名 : 被遍历的集合(Collection)或者数组) {
}
例,遍历集合。
ArrayList<String> al = new ArrayList<String>();
al.add("abc1");
al.add("abc2");
al.add("abc3");
之前我们使用迭代器遍历:
Iterator<String> it = al.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
现在我们可使用高级For循环了,主要就是为了简化书写。
for(String s : al) {
System.out.println(s);
}
结论:
高级for循环对集合进行遍历,只能获取集合中的元素,但是不能对集合进行操作。迭代器除了遍历,还可以进行remove()集合中元素的动作,如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。
对于数组int[] arr = {3,5,1};
,我们可使用传统for循环:
for (int x = 0; x < arr.length; x++) {
System.out.println(arr[x]);
}
还可使用高级for循环:
for (int i : arr) {
System.out.println("i:"+i);
}
问:传统for和高级for有什么区别呢?
答:高级for有一个局限性,必须有被遍历的目标,而该目标只能是Collection或数组。
建议:在遍历数组的时候还是希望使用传统for,因为传统for可以定义角标。
对于Map集合,我们也可以使用高级for循环。
HashMap<Integer, String> hm = new HashMap<Integer, String>();
hm.put(1, "a");
hm.put(2, "b");
hm.put(3, "c");
第一种遍历方式:
Set<Integer> keySet = hm.keySet();
for (Integer i : keySet) {
System.out.println(i+"::"+hm.get(i));
}
第二种遍历方式:
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("gaga",2,3,4,5,6);
}
public static void show(String str, int... arr) {
System.out.println(arr.length);
}
}
注意:方法的可变参数在使用时,可变参数一定要定义在参数列表的最后面。
静态导入
例,
import java.util.*;
import static java.util.Arrays.*; // 导入的是Arrays类中的所有静态成员。
import static java.lang.System.*; // 导入了System类中所有静态成员。
class StaticImport extends Object {
public static void main(String[] args) {
out.println("hello");
int[] arr = {3,1,5};
sort(arr);
int index = binarySearch(arr, 1);
System.out.println(Arrays.toString(arr)); // 此时不能省略Arrays,方法重名时,指定具备所属的对象或类。
System.out.println("index="+index);
}
}
注意:
- 当类名重名时,需要指定具体的包名。
- 当方法重名时,指定具备所属的对象或类。