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个方法:

    1. Object getKey(); 返回Entry里包含的key值
    2. Object getValue(); 返回Entry里包含的value值
    3. 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 实现的,由一个双向链表所构成
  • 排序方式有两种:
    1. 根据写入顺序排序
    2. 根据访问顺序排序: 每次 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:

特点:

  1. 该集合中的键和值都是字符串类型
  2. 集合中的数据可以保存到流中,或者从流中获取。
  3. 通常该集合用于操作以键值对形式存在的配置文件。

基本方法:

  • 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。