第十三章 集合(HashSet HashMap)

java基础13

第十三章 集合(HashSet HashMap)

13.1 Set集合

13.1.1 概述

第十三章 集合(HashSet HashMap)
java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。与 List 接口不同的是, Set 接口下的集合不存储重复的元素。Set 集合有多个子类,这里我们介绍其中的 java.util.HashSet 、java.util.LinkedHashSet 这两个集合。注意:集合取出元素的方式可以采用:迭代器、增强for。

13.1.2 Set集合存储并遍历

public static void main(String[] args) {
	 Set<String> set = new HashSet<String>(); 
	 set.add("java"); 
	 set.add("hello"); 
	 set.add("world"); 
	 set.add("kkb");
	 set.add("kaikeba");
	  Iterator<String> it = set.iterator(); 
	  while (it.hasNext()){ 
	  String str = it.next(); 
	  System.out.println(str); }
	  for(String str : set){ System.out.println(str); } 
	  }

13.1.3 HashSet集合特点

  • HashSet底层数据结构是单向哈希表。

  • 不保证元素的迭代顺序,存储元素的顺序和取出元素的顺序不一致。
    此集合不允许存储重复元素。

    • 存储在此集合中的元素应该重写hashCode()和equals()方法保证唯一性。
  • 此集合具有数组,链表,红黑树三种结构特点。

  • 线程不安全,运行速度快。

13.1.4 HashSet存储自定义类型

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一.

创建自定义Student类:

public class Student {
	 private String name; 
	 private int age; 
	 //get/set/构造方法 已省略 
	 @Override 
	 public boolean equals(Object o) { 
	 if (this == o) return true; 
	 if (o == null || getClass() != o.getClass()) 
	 return false; 
	 Student student = (Student) o; 
	 return age == student.age && Objects.equals(name, student.name); }
	 @Override public int hashCode() { 
	 return Objects.hash(name, age); 
	 } 
	}

创建测试类:

public static void main(String[] args) { 
	//创建集合对象 该集合中存储 Student类型对象
	 HashSet<Student> set = new HashSet<Student>();
	  //存储 
	  set.add(new Student("热巴", 26)); 
	  set.add(new Student("郭德纲", 44)); 
	  set.add(new Student("热巴", 26)); 
	  set.add(new Student("郭麒麟", 23)); 
	  for (Student stu : set) { System.out.println(stu); 
	  }
	   }

13.1.5 哈希表

什么是哈希表呢?
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用数组处理冲突,同一hash值的链表都存储在一个数组元素里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

  • 哈希表的初始化容量,数组长度为16个。
    • 当数组容量不够时,扩容为原数组长度的2倍
  • 加载因子为0.75。
    • 指当数组的容量被使用到长度的75%时,进行扩容。

简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
第十三章 集合(HashSet HashMap)

13.2 LinkedHashSet集合

13.2.1 概述

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。

public static void main(String[] args) { 
	Set<String> set = new LinkedHashSet<String>(); 
	set.add("bbb"); 
	set.add("aaa"); 
	set.add("abc"); 
	set.add("bbc"); 
	Iterator<String> it = set.iterator(); 
	while (it.hasNext()) { 
	System.out.println(it.next()); }
	for(String s : set){ System.out.println(s);
	}
}

13.3 Map集合

13.3.1 概述

现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即 java.util.Map 接口。
我们通过查看 Map 接口描述,发现 Map 接口下的集合与 Collection 接口下的集合,它们存储数据的形式不同,如下图。
第十三章 集合(HashSet HashMap)

  • Collection 中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
  • Map 中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的
    值。
  • Collection 中的集合称为单列集合, Map 中的集合称为双列集合。
  • 需要注意的是, Map 中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

13.3.2 Map接口中的常用方法

  • public V put(K key, V value):把指定的键与指定的值添加到Map集合中。
  • public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的
    值。
  • public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
  • public Set keySet() : 获取Map集合中所有的键,存储到Set集合中。
  • public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
  • public boolean containKey(Object key) :判断该集合中是否有此键。
  • public Collection values() 返回Map集合中的所有值到Collection集合。
public static void main(String[] args) {
 //创建 map对象 
 	HashMap<String, String> map = new HashMap<String, String>();
 	//添加元素到集合 
 	map.put("黄晓明", "杨颖"); 
 	map.put("文章", "马伊琍"); 
 	map.put("邓超", "孙俪"); 
 	System.out.println(map); 
 	//String remove(String key) 
 	System.out.println(map.remove("邓超")); 
 	System.out.println(map); 
 	// 想要查看 黄晓明的媳妇 是谁 
 	System.out.println(map.get("黄晓明")); 
 	System.out.println(map.get("邓超")); 
 	Collection<String> coll = map.values(); 
 	for(String value : coll){ 
 	System.out.println(value); 
 	} 
 	}

注意:使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中; 若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。

13.3.3 Map集合遍历

方式1:键找值方式

通过元素中的键,获取键所对应的值
分析步骤:

  1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示: keyset()
  2. 遍历键的Set集合,得到每一个键。
  3. 根据键,获取键所对应的值。方法提示: get(K key)

遍历图解:
第十三章 集合(HashSet HashMap)

方式2:键值对方式

即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
Entry键值对对象:
我们已经知道, Map 中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在 Map 中是一一对应关系,这一对对象又称做 Map 中的一个 Entry(项) 。 Entry 将键值对的对应关系封装成了对象。即键值对对象,这
样我们在遍历 Map 集合时,就可以从每一个键值对( Entry )对象中获取对应的键与对应的值。

在Map集合中也提供了获取所有Entry对象的方法:

  • public Set<Map,Entry<K,V>> entrySet():

获取到Map集合中所有的键值对对象的集合(Set集合)。获取了Entry对象 , 表示获取了一对键和值,那么同样Entry中 , 分别提供了获取键和获取值的方法:

  • public K getKey() :获取Entry对象中的键。
  • public V getValue() :获取Entry对象中的值。

操作步骤与图解:

  1. 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示: entrySet() 。
  2. 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。
  3. 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示: getkey() getValue()

遍历图解:
第十三章 集合(HashSet HashMap)

13.3.4 HashMap集合

Map接口实现类HashMap类特点:

  • 底层哈希表结构。
  • 不允许重复键。
    • 用作键的对象,应该重写hashCode()方法和equals()方法。
  • 此集合可以使用null值和null键。
  • 线程不安全,运行速度快。

13.3.5 HashMap存储自定义对象

练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。

注意,学生姓名相同并且年龄相同视为同一名学生。

编写学生类:

public class Student {
 private String name; 
 private int age; 
 //构造方法 //get/set 
 @Override 
 public boolean equals(Object o) { 
 if (this == o) return true; 
 if (o == null || getClass() != o.getClass()) 
 return false; 
 Student student = (Student) o; 
 return age == student.age && Objects.equals(name, student.name); }
 @Override 
 public int hashCode() { 
 return Objects.hash(name, age); 
 } 
}

测试类:

public static void main(String[] args) { 
	//1,创建Hashmap集合对象。 
	Map<Student,String> map = new HashMap<Student,String>(); 
	//2,添加元素。 
	map.put(new Student("lisi",28), "上海"); 
	map.put(new Student("wangwu",22), "北京"); 
	map.put(new Student("wangwu",22), "南京"); 
	//3,取出元素。键找值方式 
	Set<Student> keySet = map.keySet(); 
	for(Student key: keySet){ 
	String value = map.get(key); 
	System.out.println(key.toString()+"....."+value);
	}
	}
  • 当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法(如果忘记,请回顾HashSet存放自定义对象)。
  • 如果要保证map中存放的key和取出的顺序一致,可以使用java.util.LinkedHashMap 集合来存放。

13.3.6 LinkedHashMap介绍

我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?

在HashMap下面有一个子类LinkedHashMap,底层是哈希表双向链表,保证迭代的顺序。

public static void main(String[] args) { 
	LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(); 
	map.put("邓超", "孙俪"); 
	map.put("李晨", "范冰冰"); 
	map.put("刘德华", "热巴"); 
	Set<Entry<String, String>> entrySet = map.entrySet(); 
	for (Entry<String, String> entry : entrySet) { 
	System.out.println(entry.getKey() + " " + entry.getValue()); 
	} 
}

13.3.7 Map集合练习

需求:
计算一个字符串中每个字符出现次数。
分析:

  1. 获取一个字符串对象
  2. 创建一个Map集合,键代表字符,值代表次数。
  3. 遍历字符串得到每个字符。
  4. 判断Map中是否有该键。
  5. 如果没有,第一次出现,存储次数为1;如果有,则说明已经出现过,获取到对应的值进行++,再次存储。
  6. 打印最终结果

方法介绍
public boolean containKey(Object key) :判断该集合中是否有此键。

public static void main(String[] args) { 
//友情提示 
	System.out.println("请录入一个字符串:"); 
	String line = new Scanner(System.in).nextLine(); 
	// 定义 每个字符出现次数的方法 
	findChar(line); }
	private static void findChar(String line) { 
	//1:创建一个集合 存储 字符 以及其出现的次数 
	HashMap<Character, Integer> map = new HashMap<Character, Integer>(); 
	//2:遍历字符串 
	for (int i = 0; i < line.length(); i++) { 
		char c = line.charAt(i); 
		//判断 该字符 是否在键集中 
		if (!map.containsKey(c)) 
		{
		//说明这个字符没有出现过 
		//那就是第一次 
		map.put(c, 1); 
		} 
		else { 
		//先获取之前的次数 
		Integer count = map.get(c);
		//count++; 
		//再次存入 更新
		 map.put(c, ++count); 
		 } 
		}
		 System.out.println(map); 
		 }

13.4 可变参数

13.4.1 可变参数

如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化。

格式:

	修饰符 返回值类型 方法名(参数类型... 形参名){ }

代码演示:

public static void main(String[] args) { 
	int sum = getSum(6, 7, 2, 12, 2121); 
	System.out.println(sum); }
public static int getSum(int... arr) { 
	int sum = 0; 
	for (int a : arr) 
	{ sum += a; }
	return sum; 
	}

注意:

  • 可变参数的本质是数组。

    • 不传递参数,数组的长度是0.
    • 传递几个参数,数组的长度就是几。
  • 一个方法中只能有一个可变参数。

  • 如果方法中有多个参数,可变参数只能写在最后一位。

13.4.2 可变参数应用

在Collections中也提供了添加一些元素方法:
public static boolean addAll(Collection c, T… elements) :往集合中添加一些元素。

public static void main(String[] args) { 
	ArrayList<Integer> list = new 
	ArrayList<Integer>(); 
	//原来写法 
	//list.add(12); //list.add(14); 
	//list.add(15); //list.add(1000); 
	//采用工具类 完成 往集合中添加元素 
	Collections.addAll(list, 5, 222, 1,2); 
	System.out.println(list); 
	}

总结

搞完,收工~

上一篇:集合框架1


下一篇:滑动窗口1——无重复字符的最长字串