java基础之集合(下)
Map集合
概述:
- 该集合存储键值对。一次添加一对,而且要保证键的唯一性。
- 嵌套类摘要 static interface Map.Entry<K,V> 映射项(键*值对)。
常用方法:
添加
- value put( key,value ):返回前一个和key关联的值,如果没有返回null。
说明:添加元素,如果出现相同的键,那么后添加的值会覆盖原有键对应的值,put方法会返回被覆盖的值
删除
- void clear():清空map集合。
- value remove(Object key):根据指定的key删除这个键值对。
判断
- boolean containsKey(key);
- boolean containsValue(value);
- boolean isEmpty();
获取
- value get(key):通过键获取值,如果没有该键返回null。当然可以通过返回null,来判断是否包含指定键。
- int size():获取键值对个数。
- Collection
values() 返回此映射中包含的值的 Collection 视图。 Set<K> keySet()
返回此映射中包含的键的 Set 视图。Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。
map集合的两种取出方式;
第一种:keySet()方法
- 原理:
- 通过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("001","A1");
map.put("003","C3");
map.put("002","B2");
map.put("004","D4");
//1.先获取map集合的所有键的Set集合,keySet();
Set<String> keySet=map.keySet();
//2.有了Set集合,就可以获取其迭代器。
Iterator<String> it = keySet.iterator();
while (it.hasNext()) {
String key=it.next();
//3.有了键值可以通过map集合的get方法获取对应的值。
String value=map.get(key);
System.out.println("key:"+key+"***value:"+value);
}
}
第二种:内部接口 Map.Entry, 推荐使用
Entry是Map接口里面的一个内部接口,用于封装key* value,有3个方法:
- Object getKey(); 返回Entry里包含的key值
- Object getValue(); 返回Entry里包含的value值
- Object setValue(Object value); 设置Entry里包含的value值,并返回新设置的value值;
原理:
- 通过entrySet()方法,将键和值的映射关系(映射关系的类型就是Map.Entry类型)作为对象存储到了Set集合中,调用Set的iterator方法,此时每个Iterator对象是Map.Entry对象,对Map.Entry分离出key * value。
代码示例(接):
Set<Map.Entry<String,String>> es = map.entrySet();
Iterator<Map.Entry<String,String>> it = es.iterator();
while (it.hasNext()){
Map.Entry<String,String> me=it.next();
String key = me.getKey();
String value = me.getValue();
System.out.println(key+"::"+value);
}
第三种:values()
- 原理:通过values方法将map集合的值存入Collection集合,再使用迭代器获取
- 代码示例(接):
Collection<String> values = map.values();
Iterator<String> it = values.iterator();
while(it.hasNext()){
System. out.println(it.next());
}
HashMap
- HashMap 底层是基于数组和链表实现的, 两个重要的参数
- 容量: 默认大小是 16
- 负载因子: 0.75
- 当 HashMap 的 size > 16*0.75 时就会发生扩容(容量和负载因子都可以自由调整)
特点:
- 判断键的唯一性的方式:以元素的Hashcode值和对象的equals方法判断键的唯一性。
- 在并发环境下使用 HashMap 容易出现死循环, 多线程场景下推荐使用
ConcurrentHashMap
- 代码示例:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/*
* 将学生对象(年龄和姓名)和学生的归属地通过键与值存储到map集合中
* 注意:姓名和年龄相同的视为同一个学生。
* 保证学生的唯一性。
*/
public class HashMapDemo {
public static void main(String[] args) {
HashMap<Student, String> hs = new HashMap<Student, String>();
System.out.println("HashMap集合存储元素调用haseCode和equals过程:");
hs.put(new Student("lishi01", 21), "beijing");
// 先判断hashCode的值,相等,再判断equals是否相等,相等则key重复,value将被会覆盖,即"beijing"被"tianjin"覆盖
hs.put(new Student("lishi01", 21), "tianjin");
hs.put(new Student("lishi02", 22), "shanghai");
hs.put(new Student("lishi03", 23), "nanjing");
//先判断hashCode的值,不相等,则key不同,向集合中存入该元素
hs.put(new Student("lishi01", 24), "wuhan");
System.out.println("******************");
Iterator<Map.Entry<Student, String>> its = hs.entrySet().iterator();
while (its.hasNext()) {
Map.Entry<Student, String> es = its.next();
Student stu = es.getKey();
String addr = es.getValue();
System.out.println(stu + "......" + addr);
}
}
}
class Student {
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
//输出调用该方法对象的name
System.out.println(this.name+"...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) {
//输出调用该方法的对象与传入对象相比较的name
System.out.println(this.name+"...equals..."+((Student) obj).getName());
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;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 转换成字符串
public String toString() {
return name + ":" + age;
}
}
- 如果希望输入的键值对集合按照跟原来存入的顺序一致,就使用LinkedHashMap
LinkedHashMap
特点
- 继承于 HashMap 实现的,由一个双向链表所构成
- 排序方式有两种:
- 根据写入顺序排序
- 根据访问顺序排序: 每次 get 都会将访问的值移动到链表末尾,这样重复操作就能的到一个按照访问顺序排序的链表
数据结构
- Entry 继承于 HashMap 的 Entry,并新增了上下节点的指针,也就形成了双向链表
- accessOrder 成员变量,默认是 false,默认按照插入顺序排序,为 true 时按照访问顺序排序
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
}
final boolean accessOrder;
- 构造方法
- 实现了init()方法
- put方法
- 对其中的 recordAccess(), addEntry(), createEntry() 进行了重写
- get方法: 重写
TreeMap:
特点:
- 给map集合中的键按自然顺序进行排序
- 判断键的唯一性的方式:就是根据保证元素唯一性的依据比较方法(实现Comparable接口中根据覆盖的compareTo方法返回值,实现Comparator接口中根据覆盖的compare方法)的返回结果是否是0,是0,就是相同元素,不存。
键值自定义排序:
- 两种方法,与TreeSet中的自定义一样
- 代码示例:
/*
*需求:对学生对象的年龄进行升序排列。
*因为数据是可以以键值对形式存在的。
*所以要使用可以排序的Map集合:TreeMap.
*/
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<Student, String> tm = new TreeMap<Student, String>(new StuNameComparator());
System.out.println("TreeMap集合储存元素,调用compare方法过程:");
tm.put(new Student("lishi02", 22), "shanghai");
tm.put(new Student("lishi01", 21), "beijing");
// 1.调用compare方法,先与集合中已存在的"lishi02"比较:key不同;
// 2.再与"lishi01"比较:name相同;然后再比较age:相同;则key相同;
// 3.将value进行覆盖:"beijing"被"tianjin"覆盖
tm.put(new Student("lishi01", 21), "tianjin");
tm.put(new Student("lishi03", 23), "nanjing");
// key不同,将不会覆盖
tm.put(new Student("lishi01", 24), "wuhan");
System.out.println("********************");
Iterator<Map.Entry<Student, String>> it = tm.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Student, String> me = it.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu + "..." + addr);
}
}
}
// 采用第二种方式,进行键值排序:定义一个类实现Comparator接口,覆盖Compare方法
class StuNameComparator implements Comparator<Student> {
public int compare(Student s1, Student s2) {
System.out.println(s1.getName() + "..." + s2.getName());
int num = s1.getName().compareTo(s2.getName());
if (num == 0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}
class Student {
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 转换成字符串
public String toString() {
return name + ":" + age;
}
}
运行结果:
Properties:
特点:
- 该集合中的键和值都是字符串类型。
- 集合中的数据可以保存到流中,或者从流中获取。
- 通常该集合用于操作以键值对形式存在的配置文件。
基本方法:
- Object setProperty(String key, String value): 调用 Hashtable 的方法 put。
- String getProperty(String key) 用指定的键在此属性列表中搜索属性。
与io流相关的方法:
Set
stringPropertyNames( ): 返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。 void list( PrintStream out ) 将属性列表输出到指定的输出流。
void load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
void load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
//模拟load方法: public static void myLoad() throws Exception { Properties prop = new Properties(); BufferedReader bufr = new BufferedReader(new FileReader("info.txt" )); String line = null; while((line = bufr.readLine()) != null){ if(line.startsWith("#" )) continue; String[] arr = line.split( "="); prop.setProperty(arr[0],arr[1]); } }
void store(OutputStream out, String comments) 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
void store(Writer writer, String comments) 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。
Collections工具类:
Collectons:是集合框架的工具类,位于java.util 包,完全由在 collection 上进行操作或返回,里面的方法都是静态的。
Collections和Collection的区别
Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。
- 它有两个常用的子接口(List:对元素都有定义索引,有序的,可以重复元素;Set:不可以重复元素。无序。)
Collections是集合框架中的一个工具类。该类中的方法都是静态的
- 提供的方法中有可以对list集合进行排序,二分查找等方法。
- 通常常用的集合都是线程不安全的。因为要提高效率。
- 如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。
常用方法:
排序:
static <T extends Comparable<? super T>> void sort(List<T> list)
:根据元素的自然顺序 对指定列表按升序进行排序。static <T> void sort(List<T> list, Comparator<? super T> c)
根据指定比较器产生的顺序对指定列表进行排序。
获取:
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
根据元素的自然顺序,返回给定 collection 的最大(最小为min)元素。static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
根据指定比较器产生的顺序,返回给定 collection 的最大(最小为min)元素。static<T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
使用二分搜索法搜索指定列表,以获得指定对象。如果搜索键包含在列表中,则返回搜索键的索引;否则返回 (*(插入点) * 1)。static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
使用二分搜索法搜索指定列表,以获得指定对象。static<T> Enumeration<T> enumeration(Collection<T> c)
返回一个指定 collection 上的枚举。
//Collections的enumeration方法源码
public static <T> Enumeration<T> enumeration(final Collection<T> c) {
return new Enumeration<T>() {
private final Iterator<T> i = c.iterator();
public boolean hasMoreElements() {
return i.hasNext();
}
public T nextElement() {
return i.next();
}
};
}
修改
static void swap(List<?> list, int i, int j)
在指定列表的指定位置处交换元素。static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
使用另一个值替换列表中出现的所有某一指定值。static <T> void fill(List<? super T> list, T obj)
使用指定元素替换指定列表中的所有元素。static void shuffle(List<?> list)
使用默认随机源对指定列表进行置换。
反转方法
static void reverse(List<?> list)
反转指定列表中元素的顺序。static <T> Comparator<T> reverseOrder()
返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序。static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
返回一个比较器,它强行逆转指定比较器的顺序。
代码示例:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/*
* 需求:分别按自然顺序进行二分法查找,并打印索引,
* 按字符串长度进行二分法查找,并打印索引,并演示
* Collections的sort方法和binarySearch方法的实现过程。
*/
class CollectionsDemo {
public static void main(String[] args) {
binarySearchDemo();
}
public static void binarySearchDemo() {
List<String> list = new ArrayList<String>();
list.add("abcd");
list.add("aaa");
list.add("cc");
list.add("kkk");
list.add("qq");
list.add("z");
System.out.println("按自然顺序进行二分法查找,并打印索引结果示例:");
// 按自然顺序排序,并打印
Collections.sort(list);
System.out.println(list);
// 按自然顺序进行二分法查找,并打印索引
// int index =Collections.binarySearch(list,"cc");//
int index = halfSearch(list, "ccc");
System.out.println("index=" + index);
System.out.println("");
System.out.println("按字符串长度进行二分法查找,并打印索引结果示例:");
// 按字符串长度排序,并打印
//Collections.sort(list, new StrLenComparator());//根据指定比较器产生的顺序(字符串长度)对指定列表进行排序
//自定义按字符串长度排序方法
mySort(list,new StrLenComparator());
System.out.println(list);
// 按指定的比较器(按字符串长度)对列表进行二分法查找,并打印索引
// int lenIndex =Collections.binarySearch(list,"cc",new StrLenComparator());
//自定义按字符串长度进行二分法查找,并打印索引
int lenIndex = halfSearch2(list, "ccc", new StrLenComparator());
System.out.println("lenIndex=" + lenIndex);
}
public static <T> void mySort(List<T> list,Comparator<? super T> comp) {
for(int i = 0; i < list.size() * 1; i++){
for(int j = i + 1; j < list.size(); j++){
if(comp.compare(list.get(i),list.get(j))>0){
Collections. swap(list ,i,j);
}
}
}
}
public static int halfSearch(List<String> list, String key) {
int max, min, mid;
max = list.size() * 1;
min = 0;
while (min <= max) {
mid = (min + max) >> 1;
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; //模拟返回值:*(插入点) * 1
}
public static int halfSearch2(List<String> list, String key,
Comparator<String> cmp) {
int max, min, mid;
max = list.size() * 1;
min = 0;
while (min <= max) {
mid = (min + max) >> 1;
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; //模拟返回值:*(插入点) * 1
}
}
// 采用比较器,根据字符串长度进行排序,若长度相等,则按自然排序
class StrLenComparator implements Comparator<String> {
public int compare(String s1, String s2) {
if (s1.length() > s2.length())
return 1;
if (s1.length() < s2.length())
return *1;
return s1.compareTo(s2);
}
}
运行结果:
Arrays:
Arrays是集合框架中的工具类,里面的方法都是静态的,包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。
数组和集合的互转:
数组转集合;
- static
List asList(T… a) 返回一个受指定数组支持的固定大小的列表。 可以使用集合的方法操作数组。
注意:数组的长度是固定的,所以对于结合的增删方法是不可以使用的,否则,会发生UnsupportedOperationException。- 如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行集合存储。
- 如果数组中的元素是基本类型数值,那么会将该数组作为集合中的元素进行存储。
int[] arr1 = {31,11,51,61}; List< int[]> list1 = Arrays.asList(arr1); System.out.println(list1); //[[I@19bb25a] Integer[] arr2 = {31,11,51,61}; List list2 = Arrays.asList(arr2); System.out.println(list2); //[31, 11, 51, 61]
集合转数组:
- 使用Collection接口中的toArray方法,可以对集合中的元素操作的方法进行限定,不允许对其进行增删
- 需要传入一个指定类型的数组
- 长度定义:
- 如果长度小于集合的size,那么该方法会创建一个同类型并和集合相同的size的数组。
- 如果长度大于集合的size,那么该方法就会使用指定的数组,存储集合中的元素,其他位置默认为null。
- 所以建议,最后长度就指定为,集合的size。