(高级基础)10_集合

一、Java集合框架概述

1.集合的引入

①数组在内存存储方面的特点:
数组初始化以后,长度就确定了。
数组声明的类型,就决定了进行元素初始化时的类型
②数组在存储数据方面的弊端:
数组初始化以后,长度就不可变了,不便于扩展;
数组中提供的属性和方法少,不便于进行添加、删除、插入等操作, 且效率不高。同时无法直接获取存储元素的个数;
数组存储的数据是有序的、可以重复的。 ---->存储数据的特点单一

Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组

2.集合的体系

Java 集合可分为 Collection 和 Map 两种体系
①Collection接口: 单列数据, 定义了存取一组对象的方法的集合
List: 元素有序、可重复的集合
Set: 元素无序、不可重复的集合
②Map接口: 双列数据,保存具有映射关系“key-value对”的集合

3.Collection接口继承树

在这里插入图片描述

4.Map接口继承树

在这里插入图片描述
一些集合允许重复元素,而其他集合不允许
一些集合时有序的 一些是无序

二、Collection接口方法

1.Collection 接口

①Collection 接口是 List、 Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
②JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如: Set和List)实现。
③在 Java5 之前, Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理; 从 JDK 5.0 增加了泛型以后, Java 集合可以记住容器中对象的数据类型

2.Collection 接口方法

1、 添加
add(Object obj)
addAll(Collection coll)
2、 获取有效元素的个数
int size()
3、 清空集合
void clear()
4、 是否是空集合
boolean isEmpty()
5、 是否包含某个元素
boolean contains(Object obj): 是通过元素的equals方法来判断是否是同一个对象
boolean containsAll(Collection c): 也是调用元素的equals方法来比较的。 拿两个集合的元素挨个比较。
6、删除
boolean remove(Object obj) : 通过元素的equals方法判断是否是要删除的那个元素。 只会删除找到的第一个元素
boolean removeAll(Collection coll): 取当前集合的差集
7、取两个集合的交集
boolean retainAll(Collection c): 把交集的结果存在当前集合中,不影响c
8、 集合是否相等
boolean equals(Object obj)
9、 转成对象数组
Object[] toArray()
10、获取集合对象的哈希值
hashCode()
11、遍历

iterator(): 返回迭代器对象,用于集合遍历

import java.util.ArrayList;
import java.util.Collection;

public class CollectMethodTest {
	public static void main(String[] args) {
		Collection c = new ArrayList();
//		1、 添加
//		add(Object obj)
		c.add(1);
		c.add(true);
		c.add(12.3);
		c.add("java");
		
		Collection c1 = new ArrayList();
		c1.add("andriod");
		c1.add("hadoop");
		c1.add("spark");
		c1.add("python");
//		addAll(Collection coll)
		c.addAll(c1);
		for(Object obj : c) {
			System.out.println(obj);
		}	
//		2、 获取有效元素的个数
//		int size()
		System.out.println("-------"+ c.size());
//		3、 清空集合
//		void clear()
//		c.clear();
//		System.out.println("-------"+ c.size());
//		4、 是否是空集合
//		boolean isEmpty()   集合为空则返回true  否则返回false
		Collection c2 = new ArrayList();
		c2.add("andriod");
		c2.add("hadoop");
		c2.add("spark");
		c2.add("python");
		System.out.println(c2.isEmpty());
//		5、 是否包含某个元素
//		boolean contains(Object obj): 是通过元素的equals方法来判断是否是同一个对象
		System.out.println(c.contains("javaee"));
//		boolean containsAll(Collection c): 也是调用元素的equals方法来比较的。 拿两个集合的元素挨个比较。
		System.out.println(c.containsAll(c2));
//		6、删除
//		boolean remove(Object obj) : 通过元素的equals方法判断是否是要删除的那个元素。 只会删除找到的第一个元素
//		c.remove("java");
//		System.out.println("********************************8");
//		c.removeAll(c1);
//		for(Object obj : c) {
//			System.out.println(obj);
//		}	
		//		boolean removeAll(Collection coll): 取当前集合的差集
//		7、取两个集合的交集
//		boolean retainAll(Collection c): 把交集的结果存在当前集合中,不影响c 将交集保存在原集合中
//		c.retainAll(c1);
//		System.out.println("********************************8");
//		for(Object obj : c) {
//			System.out.println(obj);
//		}	
//		8、 集合是否相等
//		boolean equals(Object obj) 判断是两个集合所包含的元素是否相同
		System.out.println(c.equals(c1));
		System.out.println(c1.equals(c2));
//		9、 转成对象数组
//		Object[] toArray()
		Object[] arr = c.toArray();
		for(int i = 0 ; i < arr.length;i++) {
			System.out.println(arr[i]);
		}
//		10、获取集合对象的哈希值
//		hashCode()
		System.out.println(c.hashCode());
//		11、遍历
//		iterator(): 返回迭代器对象,用于集合遍历
	}
}

三、Iterator迭代器接口

Iterator 一个迭代器 是专门用来迭代集合

1、使用:

1 获取该集合的迭代器Iterator

Iterator iter = c.iterator();

在这里插入图片描述

Iterator iter = c.iterator();
			System.out.println(iter.hasNext());
			Object obj1 = iter.next();
			System.out.println(obj1);
			System.out.println(iter.hasNext());
			Object obj2 = iter.next();
			System.out.println(obj2);

当迭代的超出集合范围 :NoSuchElementException
在这里插入图片描述

while(iter.hasNext()) {
			System.out.println(iter.next());
		}
for(Iterator ite  = c.iterator();ite.hasNext();) {
		Object obj = ite.next();
		System.out.println(obj);
	}

for循环更好(迭代器生命周期短)
但是在实际使用中 使用while

2、迭代器的工作原理

在这里插入图片描述
使用迭代器时 需要注意的问题:

Iterator iter = c.iterator();
		while(iter.hasNext()) {
			//在使用时 判断一次 移动一次 不能判断一次 移动多次
			Object obj = iter.next();//调用一次next方法就移动一次
			System.out.println(iter.next());
			
		}

①使用迭代器移除元素
在这里插入图片描述

Iterator iter = c.iterator();
		while(iter.hasNext()) {
			Object obj = iter.next();
			if(obj.equals(12.3)) {
				iter.remove();
			}else {
				System.out.println(obj);	
			}
			
			
		}

②使用迭代器迭代 使用集合删除

	Iterator iter = c.iterator();
	while(iter.hasNext()) {
		Object obj = iter.next();
		if(obj.equals(12.3)) {
			c.remove(12.3);
		}else {
			System.out.println(obj);	
		}
}

在这里插入图片描述
注意:
1、Iterator可以删除集合的元素, 但是是遍历过程中通过迭代器对象的remove方法, 不是集合对象的remove方法。
2、如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
在这里插入图片描述
注意:
在这里插入图片描述
Collection集合中 只能保存Object及其子类
Collection只能保存对象 而不能保存基本类型

Collection c = new ArrayList();
//		1、 添加
//		add(Object obj)
		c.add(1);
		c.add(true);
		c.add(12.3);
		c.add("java");

上述代码中的基本类型都是其对应的包装类 (自动装箱 自动拆箱)
数组是可以保存基本类型的 :
int[] arr = new int[10];

小结:

集合:
Collection:单列
Map:双列 映射 key-value
Collection:List Set
Collection:
添加 删除 清空 获取长度 判断 交集 迭代

四、Collection子接口之一:List接口

1.List接口概述

有序集合(也称为序列 )。
②可以精确控制列表中每个元素的插入位置
③列表通常允许重复的元素

2.List的常用方法

void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index位置开始将eles中
的所有元素添加进来
Object get(int index):获取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出现的位置
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合

public static void main(String[] args) {
		//创建List集合
		List list  = new ArrayList();
		list.add("hello");
		list.add("wolrd");
		list.add("java");
		list.add(null);
		list.add(null);
		list.add(null);
//		void add(int index, Object ele):在index位置插入ele元素  这里也可以添加一个集合
		//		list.addAll(2,c); 将集合整体作为list元素
		list.add(1,"IOS");
		Collection c = new ArrayList();
		c.add(1);
		c.add(2);
		c.add(3);
		c.add(4);
//		boolean addAll(int index, Collection eles):从index位置开始将eles中
//		的所有元素添加进来  
		list.addAll(2,c);
		System.out.println(list.size());
//		Object get(int index):获取指定index位置的元素
//		int indexOf(Object obj):返回obj在集合中首次出现的位置
		System.out.println("--------------++++"+list.indexOf("IOS"));
//		int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
		list.add(5,"IOS");
		System.out.println("----------------+++++"+list.lastIndexOf("IOS"));
//		Object remove(int index):移除指定index位置的元素,并返回此元素
		list.remove(5);
//		Object set(int index, Object ele):设置指定index位置的元素为ele
		list.set(1, "Hadoop");
//		List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
		
		//遍历
		for(Object obj : list) {
			System.out.println(obj);
		}
		List subList = list.subList(3, 7);
		System.out.println("子集遍历");
		for(int i = 0 ; i < subList.size(); i++) {
			System.out.println(subList.get(i));
		}
		System.out.println("****************************");
		for(int i = 0 ; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
		System.out.println("****************************");
		Iterator iter = list.iterator();
		while(iter.hasNext()) {
			System.out.println(iter.next());
		}
		
	}
}

3.list集合中的特点:

1 允许存在重复元素
2 允许null元素的存在 并且可以存储多个null
3 list 是有序集合 (有序:存入顺序和遍历顺序一致
4 可以通过索引来精确控制集合中的元素 并且索引是从0开始
5 通过get(int index) 获取元素的时候 如果超出了列表的长度 则会出现异常IndexOutOfBoundsException

4.List实现类之一: ArrayList

1 ArrayList 本质:Object类型的数组
2 在jdk1.8 中 如果没有指定list的长度 则会首先在创建list对象的时候 创建一个长度为0的空数组 当第一次调用add方法时 则会为列表分配空间10
jdk1.7的时候 在创建列表对象的时候 会直接创建一个长度为10的数组
3 当列表初始容量满的时候 则会自动扩容 按照初始容量的1.5倍进行扩充
4 列表长度可变的本质:就是按照扩容机制 创建一个新的数组 并且将原数组复制到新数组(扩容之后的数组)
5 ArrayList 该实现不是线程安全的

常用方法:
trimToSize()方法的作用: 就是将list的大小 改变为列表中元素所占用空间的实际大小
ListIterator中的方法:

在这里插入图片描述

实现list的逆序遍历 前提时必选先正序遍历

public static void main(String[] args) {
		List list = new ArrayList();
		list.add("hello");
		list.add("java");
		list.add("ios");
		list.add("hadoop");
		list.add("spark");
		// 获取特有的迭代器
		ListIterator  iter = list.listIterator();
		while(iter.hasNext()) {
			System.out.println(iter.next());
		}
		System.out.println("******************************");	
		while(iter.hasPrevious()) {
			System.out.println(iter.previous());
		}
	}

使用迭代器添加元素:

// 获取特有的迭代器
		ListIterator  iter = list.listIterator();
		while(iter.hasNext()) {
			Object obj = iter.next();
			if(obj.equals("ios")) {
				iter.add("andriod");
				iter.previous();
			}
			System.out.println(obj);
		}

添加完成之后 如果想要获取新增元素 需要调用iter.previous();
注意事项
如果在使用迭代器迭代集合的时候 如果使用集合的add方法 则会出现

在这里插入图片描述
解决异常的方案:
使用迭代器迭代 则对集合的修改 应该使用迭代器

5.List实现类之二: LinkedList

1 LinkedList 的实现采用的是双向链表
2 插入删除数据速度快 但是遍历慢(对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高)

新增方法:
void addFirst(Object obj)
void addLast(Object obj)
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()

public static void main(String[] args) {
		LinkedList list = new LinkedList();
		list.add("hello");
		list.add("java");
		list.add("ios");
		list.add("hadoop");
		list.add("spark");
		list.addFirst("Python");//将当前元素增加到链表头
		list.addFirst("AAAA");
		list.addLast("BBBB");//将当前元素增加到链表尾部
		list.removeFirst();
		list.removeLast();
		System.out.println(list.getFirst());//获取头元素
		System.out.println(list.getLast());//获取尾元素
		Iterator  iter = list.iterator();
		while(iter.hasNext()) {
			System.out.println(iter.next());
		}
	}

6.List 实现类之三: Vector

Vector 是一个古老的集合, JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于Vector是线程安全的
在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList; Vector总是比ArrayList慢,所以尽量避免使用。

面试题:
请问ArrayList/LinkedList/Vector的异同? 谈谈你的理解? ArrayList底层是什么?扩容机制?Vector和ArrayList的最大区别 ==
ArrayList和LinkedList的异同
二者都线程不安全,相对线程安全的Vector,执行效率高。
此外, ArrayList是实现了基于动态数组的数据结构, LinkedList基于链表的数据结构。对于随机访问get和set, ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove, LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和Vector的区别
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。== Vector还有一个子类Stack

五、Collection子接口之二:Set接口

1、特点

1.不包含重复元素的集合
2.set集合是无序
3.set集合中也可以存储null 但是只能存储一个
4.在set中判断元素是否重复 使用equals()方法
如果两个对象的equals方法返回true 则他们的hashcode值应该也是相等的
换句话来说 :如果两个对象的equals返回是true 但是hashcode不一致 则也会认为元素不重复

在自定义对象的时候 都需要去重写Object的hashcode 和equals方法

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 boolean equals(Object obj) {
//		Student stu = (Student)obj;
//		if(this.name .equals(stu.name)) {
//			return true;
//		}
//		return false;
//	}
	
	
	@Override
	public String toString() {
		return "Student [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;
		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 static void main(String[] args) {
		//创建set集合对象
		Set set = new HashSet();
		set.add("ccc");
		set.add("aaa");
		set.add("1111");
		set.add("aaa");
		set.add("eeee");
		set.add("aaa");
		set.add(null);
		set.add("bbb");
		set.add("bbb");
		set.add(null);
		set.add("bbb");
		set.add(null);
		Student stu1 = new Student("张三",20);
		Student stu2 = new Student("张三",21);
		Student stu3 = new Student("张三",23);
		Student stu4 = new Student("张三",21);
		Student stu5 = new Student("张三",21);
		set.add(stu1);
		set.add(stu2);
		set.add(stu3);
		set.add(stu4);
		set.add(stu5);
		
		Iterator iter = set.iterator();
		while(iter.hasNext()) {
			System.out.println(iter.next());
		}
	}

面试题:Set集合中 判断元素是否重复的机制是什么?
小结:
集合:
Collection
Map
Colletion :List Set
List :有序 可重复 多个null
ArrayList :可变数组
LinkedList:双向链表
Vector:可变数组
Set:无序 不可重复 一个null
HashSet
LinkedHashSet
TreeSet

2.Set实现类之一: HashSet

1.此实现不同步 (不是线程安全的)
2.基于HashMap来实现的
3.初始容量为16  负载因子为0.75 扩容两倍(底层也是数组, 初始容量为16, 当如果使用率超过0.75, (16*0.75=12)就会扩大容量为原来的2倍。 )

在这里插入图片描述
当调用HashSet的add方法时 是将要保存的元素保存在HashMap key的位置

1.重写 hashCode() 方法的基本原则:
在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode()方法的返回值也应相等。
对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值

2.重写 equals() 方法的基本原则
以自定义的Customer类为例,何时需要重写equals()?
当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是, 根据Object.hashCode()方法,它们仅仅是两个对象。
因此,违反了“相等的对象必须具有相等的散列码”。
结论:复写equals方法的时候一般都需要同时复写hashCode方法。 通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

3.Set实现类之二: LinkedHashSet

LinkedHashSet 是 HashSet 的子类
LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。(有序)

效率低于HashSet
不允许集合元素重复

4.Set实现类之三: TreeSet

基于TreeMap
TreeSet底层使用红黑树结构存储数据

特点:
有序,查询速度比List快
有序指的是他的自然顺序(不是存储顺序)
所有的基本类型的包装类 及String类都实现了一个Comparable接口
该接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法 。

1.排 序—自然排序

当需要将自定义对象保存到TreeSet集合的时候
1 该类必须实现Comparable 并且要重写compareTo 在compareTo方法中来定义针对该类对象的排序规则
2 在TreeSet针对字符的排序 如果是字母 则按照字典顺序 如果是汉字 则按照汉字的Unicod码从小到大排序
3.因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类的对象。

/*
	 * 优先按照姓名排序  如果姓名相同  则按照年龄排序
	 */
	@Override
	public int compareTo(Object o) {
		Student stu = (Student)o;
		int result = this.name.compareTo(stu.getName());
		if( result == 0) {
			result = this.age.compareTo(stu.age);
		}
		
		return result;
	}

2.排 序—定制排序

在这里插入图片描述
使用外部类来实现定制排序

public class StuComparator implements Comparator {

	@Override
	public int compare(Object o1, Object o2) {
		 Student stu1 = (Student)o1;
		 Student stu2 = (Student)o2;
		int result = stu1.getName().compareTo(stu2.getName());
		if(result == 0 ) {
			result = stu1.getAge() - stu2.getAge();
		}
		return result;
	}

}
public class TreeSetTest {
	public static void main(String[] args) {
		TreeSet set = new TreeSet(new StuComparator());

		Student stu1 = new Student("张三",21);
		Student stu2 = new Student("张三",23);
		Student stu3 = new Student("王五",18);
		Student stu4 = new Student("赵四",20);
		Student stu5 = new Student("刘能",22);
		set.add(stu1);
		set.add(stu2);
		set.add(stu3);
		set.add(stu4);
		set.add(stu5);
		for(Object obj : set) {
			System.out.println(obj);
		}
	}
}

第二种实现方式(使用成员内部类来实现定制排序)

public class TreeSetTest {
	public static void main(String[] args) {
		TreeSet set = new TreeSet(new TreeSetTest().new StuComparator1());

		Student stu1 = new Student("张三",21);
		Student stu2 = new Student("张三",23);
		Student stu3 = new Student("王五",18);
		Student stu4 = new Student("赵四",20);
		Student stu5 = new Student("刘能",22);
		set.add(stu1);
		set.add(stu2);
		set.add(stu3);
		set.add(stu4);
		set.add(stu5);
		for(Object obj : set) {
			System.out.println(obj);
		}
	}
	//使用成员内部类来实现定制排序
	public class StuComparator1 implements Comparator{

		@Override
		public int compare(Object o1, Object o2) {
			Student stu1 = (Student)o1;
			 Student stu2 = (Student)o2;
			int result = stu1.getName().compareTo(stu2.getName());
			if(result == 0 ) {
				result = stu1.getAge() - stu2.getAge();
			}
			return result;
		}
		
	}
}

实现方式三:匿名内部类

public class TreeSetTest {
	public static void main(String[] args) {
		//使用匿名内部类来实现比较
		TreeSet set = new TreeSet(new Comparator() {

			@Override
			public int compare(Object o1, Object o2) {
				Student stu1 = (Student)o1;
				 Student stu2 = (Student)o2;
				int result = stu1.getName().compareTo(stu2.getName());
				if(result == 0 ) {
					result = stu1.getAge() - stu2.getAge();
				}
				return result;
			}
		});

		Student stu1 = new Student("张三",21);
		Student stu2 = new Student("张三",23);
		Student stu3 = new Student("王五",18);
		Student stu4 = new Student("赵四",20);
		Student stu5 = new Student("刘能",22);
		set.add(stu1);
		set.add(stu2);
		set.add(stu3);
		set.add(stu4);
		set.add(stu5);
		for(Object obj : set) {
			System.out.println(obj);
		}
	}
	
}

随堂练习:
在List内去除重复数字值,要求尽量简单
拓展:去除List集合中的重复元素 并对集合中的元素进行排序(TreeSet特性)

小结:

Set 无序 不可重复
HashSet:hashMap
hashCode决定元素在数组中的位置 在存入元素时 首先计算该元素的hashCode值 hashCode决定了该元素在数组中存储的位置
如果该HashCode值 与当前数组中所有的元素的hashCode都不相同 直接将元素保存到set中
如果HashCode 与当前数组中某一个元素的hasCode相同,
则需要调用其equals方法
equals返回true 此时判定为重复 不保存
equals返回false 则认为不重复 保存可以成功 存储的方式是采用链表
LinkedHashSet:HashSet的子类 在其中采用双向链表来维护元素的存入顺序
LinkedHashSet 效率低于HashSet
TreeSet:TreeMap 红黑树结构
TreeSet要求对于存入其中的元素要实现一个比较器
排序方式:自然排序 定制排序
在排序中 对数组 英文 采用字典顺序
汉字 采用汉字的Unicode码进行排序
定制排序:在定义容器的时候 为容器来指定所使用的比较器
1 外部类来实现
2 采用成员内部类
3 匿名内部类。

六:Map接口

Map 是双列 反应的是映射关系 key - value 函数 y = f(x)
在这里插入图片描述

1.Map接口概述

Map与Collection并列存在。用于保存具有映射关系的数据:key-value
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 key 用Set来存放, 不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法
常用String类作为Map的“键”
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value
Map接口的常用实现类: HashMap、 TreeMap、 LinkedHashMap和Properties。

其中, HashMap是 Map 接口使用频率最高的实现类
在这里插入图片描述

2.常用方法

①添加、 删除、修改操作:

Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
void putAll(Map m):将m中的所有key-value对存放到当前map中
Object remove(Object key):移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据

②元素查询的操作:

Object get(Object key):获取指定key对应的value
boolean containsKey(Object key):是否包含指定的key
boolean containsValue(Object value):是否包含指定的value
int size():返回map中key-value对的个数
boolean isEmpty():判断当前map是否为空
boolean equals(Object obj):判断当前map和参数对象obj是否相等

Map中存入元素
1 当前key如果不存在 则直接保存
2 如果key已经存在 则会使用新的value 替换原来的key所映射的value
3 key 不允许重复 key是用set集合进行保存的 Value是可以重复的
4 允许null键和null值的存在 但是key中null 只能有一个 value中的null可以多个

③元视图操作的方法:

Set keySet():返回所有key构成的Set集合
Collection values():返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set集合

key —value 一对一关系
value - -key 一对多关系

public static void main(String[] args) {
	Map map = new HashMap();
	//存入元素
	map.put("1", "AAA");
	map.put("4", "DDD");
	map.put("2", "BBB");
	map.put("3", "CCC");
	map.put("5", "EEE");
	System.out.println(map.size());
	//map.remove("3");
	System.out.println(map.size());
	//通过key获取value
	System.out.println(map.get("1"));
	//判断当前集合中是否包指定的key
	System.out.println(map.containsKey("0"));
	//判断当前集合中是否包指定的value
	System.out.println(map.containsValue("AAA"));
	
	//map中key  value的特点
	map.put("3", "HHHH");
	System.out.println(map.containsValue("CCC"));//true
	System.out.println(map.size());
	map.put("6", "HHHH");
	System.out.println(map.size());
	map.put(null,null);
	map.put("7",null);
	map.put("8",null);
	System.out.println(map.size());
	
	//Set keySet():返回所有key构成的Set集合
	Set set = map.keySet();
	//第一种遍历方式
	for(Object key : set) {
		System.out.println(key +"----"+map.get(key));
	}
	//Collection values():返回所有value构成的Collection集合
	Collection c = map.values();
	for(Object value : c) {
		System.out.println(value);
	}
	//Set entrySet():返回所有key-value对构成的Set集合
	//map的第二种遍历方式
	Set entrySet = map.entrySet();
	Iterator iter = entrySet.iterator();
	while(iter.hasNext()) {
		Map.Entry  entry = (Entry) iter.next();
		System.out.println(entry.getKey() +"-----"+entry.getValue());
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.Map实现类之一: HashMap

HashMap 使用频率最高的实现
HashMap是 Map 接口使用频率最高的实现类。
底层使用哈希表结构
允许使用null键和null值,与HashSet一样,不保证映射的顺序。
所有的key构成的集合是Set:无序的、不可重复的。所以, key所在的类要重写:equals()和hashCode()
所有的value构成的集合是Collection:无序的、可以重复的。所以, value所在的类要重写: equals()
一个key-value构成一个entry
所有的entry构成的集合是Set:无序的、不可重复的
HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
非线程安全

1.HashMap的存储结构:

jdk7:数组+ 链表
jdk8: 数组 + 链表 + 红黑树
(存储的整体称为桶)

以jdk7 为例,保存元素:
1 集合的初始化:创建HashMap 时 会初始化一个容量为16的数组 负载因子 0.75 临界值(阈值)扩充的临界值 12

jdk8 初始化时  并不会立即创建一个容量为16的数组 
而是在第一次put元素的时候才会初始化数组分配空间。

2 元素的保存:
①当保存元素时 首先会根据key来计算出hashCode值 hashCode决定了元素(Entry)在数组中的保存位置
②当key的hashCode值 与当前数组中的所有的元素的hashCode都不相同 则直接保存
③当key的hashCode值 与当前数组中的某一个或多个的hashCode相同时。此时并不会直接判定保存失败。
④此时需用来使用该key的equals方法 来判定 如果equals返回的是true不会保存
(此时 value 会使用新值来替换旧值)
⑤equals返回的时false 会以链表的形式保存元素

在这里插入图片描述
jdk7和8的区别:
1.数组类型不同:
jdk7 使用的时数组+ 链表
Entry[] table
jdk8 使用的时数组 + 链表 + 红黑树
Node[] table

2.在jdk8的时候 当链表的长度大于8时 则将链表转换为红黑树 红黑树的引入 提升元素检索效率
3.JDK1.8中,形成链表结构时,新添加的key-value对在链表的尾部(七上八下)
4.初始化数组不同:
JDK1.7中 创建HashMap 时 会初始化一个容量为16的数组
JDK1.8中默认情况下,先不创建长度为16的数组,当首次调用map.put()时,再创建长度为16的数组

扩容机制:
当当前数组的容量的使用达到原数组的3/4时 则就扩容
扩容的时机 是由其中的临界值(阈值)来决定 扩容为原来的2倍 并将原来的元素复制到扩容之后的集合中

JDK1.8中:
当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。

关于映射关系的key是否可以修改? answer:不要修改
映射关系存储到HashMap中会存储key的hash值,这样就不用在每次查找时重新计算每一个Entry或Node(TreeNode)的hash值了,因此如果已经put到Map中的映射关系,再修改key的属性,而这个属性又参与hashcode值的计算,那么会导致匹配不上

面试题:
谈谈你对HashMap中put/get方法的认识?如果了解再谈谈HashMap的扩容机制?默认大小是多少?什么是负载因子(或填充比)? 什么是吞吐临界值(或阈值、 threshold)?
面试题:负载因子值的大小,对HashMap有什么影响
负载因子的大小决定了HashMap的数据密度。
负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入时的比较次数增多,性能会下降。
负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。
按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此时平均检索长度接近于常数

在这里插入图片描述
(相当于n = n | n >>>1)

4.Map实现类之二: LinkedHashMap

LinkedHashMap 是 HashMap 的子类
在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
与LinkedHashSet类似, LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致

非线程安全
HashMap中的内部类: Node

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
}

LinkedHashMap中的内部类: Entry

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

5.Map实现类之三: TreeMap

①TreeMap存储 Key-Value 对时, 需要根据 key-value 对进行排序。
TreeMap 可以保证所有的 Key-Value 对处于有序状态。
TreeSet底层使用红黑树结构存储数据
非线程安全
TreeMap 的 Key 的排序:
自然排序: TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现Comparable 接口
TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。

6.Map实现类之四: Hashtable

Hashtable是个古老的 Map 实现类, JDK1.0就提供了。不同于HashMap,
Hashtable是线程安全的
Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
与HashMap不同, Hashtable 不允许使用 null 作为 key 和 value
与HashMap一样, Hashtable 也不能保证其中 Key-Value 对的顺序
Hashtable判断两个key相等、两个value相等的标准, 与HashMap一致。

7.Map实现类之五: Properties

Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、 value 都是字符串类型,所以Properties 里的 key和 value 都是字符串类型
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

public static void main(String[] args) throws FileNotFoundException, IOException {
	Properties p = new Properties();
	p.load(new FileInputStream(new File("jdbc.properties")));
	String username = p.getProperty("username");
	String password = p.getProperty("password");
	String driver = p.getProperty("driver");
	System.out.println(username);
	System.out.println(password);
	System.out.println(driver);
}

七:Collections工具类

Collections 是一个操作 Set、 List 和 Map 等集合的工具类
Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

排序操作: (均为static方法)
reverse(List): 反转 List 中元素的顺序
shuffle(List): 对 List 集合元素进行随机排序
sort(List): 根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List, Comparator): 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List, int, int): 将指定 list 集合中的 i 处元素和 j 处元素进行交换

1.Collections常用方法

查找、替换:
Object max(Collection): 根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection, Comparator): 根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection, Comparator)
int frequency(Collection, Object): 返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list, Object oldVal, Object newVal): 使用新值替换List 对象的所有旧值

类中所有的方法都是static

public static void main(String[] args) {
		List list = new ArrayList();
		list.add(2);
		list.add(3);
		list.add(1);
		list.add(5);
		list.add(4);
//		排序操作: (均为static方法)
//		reverse(List): 反转 List 中元素的顺序
		Collections.reverse(list);
//		shuffle(List): 对 List 集合元素进行随机排序
		Collections.shuffle(list);
//		sort(List): 根据元素的自然顺序对指定 List 集合元素按升序排序
		Collections.sort(list);
//		sort(List, Comparator): 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
		Collections.sort(list, new Comparator() {

			@Override
			public int compare(Object o1, Object o2) {
			 Integer i1 = (Integer)o1;
			 Integer i2 = (Integer)o2;
				return -i1.compareTo(i2);
			}
		});
//		swap(List, int, int): 将指定 list 集合中的 i 处元素和 j 处元素进行交换
		Collections.swap(list, 2, 3);
		for(Object obj : list) {
			System.out.println(obj);
		}
	
	}

Copy 在复制时 目标数组不能为空 而且元素的个数必须大于等于源集合的元素个数

List list1 = new ArrayList ();
		list1.add("aaa");
		list1.add("aaa");
		list1.add("aaa");
		list1.add("aaa");
		list1.add("aaa");
		list1.add("aaa");
		list1.add("aaa");
		Collections.copy(list1, list);

否则会抛出异常:
在这里插入图片描述

2.Collections常用方法:同步控制

Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值