1. Set集合
1.1 Set集合存储元素不重复的原理
Set集合在调用add方法的时候,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复。
public class DemoHashSetSaveString {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
String s1 = new String("abc");
String s2 = new String("abc");
//add方法会调用s1的hashCode方法,计算字符串“abc”的哈希值
//然后在集合中找有没有哈希值和这个哈希值一样的元素,发现没有,就把s1存储到集合中
set.add(s1);
//add方法会调用s2的hashCode方法,计算字符串“abc”的哈希值
//然后在集合中找有没有哈希值和这个哈希值相同的元素,发现有(哈希冲突)
//s2会调用equals方法和哈希值相同的元素进行比较,即s2.equals(s1),返回true,即两元素哈希值相同,且equals方法返回true,就认定两元素相同,就不会把s2存储到集合中
set.add(s2);
//add方法会调用“123”的hashCode方法,计算字符串“123”的哈希值
//然后在集合中找有没有哈希值和这个哈希值一样的元素,发现没有,就把“123”存储到集合中
set.add("123");
//同s2
set.add("abc");
System.out.println(set); //[123, abc]
}
}
1.2 HashSet存储自定义类型的元素
set集合保证元素唯一:存储的元素,必须重写hashCode方法和equals方法
import java.util.Objects;
public class Person {
private int age;
private String name;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
若要保证存储的元素不重复,自己定义的类中需要重写hashCode方法和equals方法。
import java.util.HashSet;
public class DemoHashSetSavePerson {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
Person p1 = new Person(9,"猪猪侠");
Person p2 = new Person(9,"猪猪侠");
Person p3 = new Person(10,"猪猪侠");
//要求:同名同年龄的人只存储一次
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set); //[Person{age=9, name='猪猪侠'}, Person{age=10, name='猪猪侠'}]
}
}
1.3 LinkedHashSet集合
java.util.LinkedHashSet集合 extends HashSet集合
LinkedHashSet集合特点: 底层是一个哈希表(数组+链表/红黑树)+链表,多了一条链表用来记录元素的存储顺序,保证元素有序。
import java.util.HashSet;
import java.util.LinkedHashSet;
public class DemoLinkedHashSet {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("www");
set.add("abc");
set.add("abc");
set.add("ppp");
System.out.println(set); //[ppp, abc, www],无序不允许重复
LinkedHashSet<String> linked = new LinkedHashSet<>();
linked.add("www");
linked.add("abc");
linked.add("abc");
linked.add("ppp");
System.out.println(linked); //[www, abc, ppp],有序不允许重复
}
}
1.4 可变参数
可变参数是JDK1.5之后出现的新特性。
使用前提: 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数。
使用格式: 定义方法的时候使用
修饰符 返回值类型 方法名(数据类型…参数名){}
可变参数的原理: 可变参数底层就是一个数组,根据传递参数的个数不同,会创建不同长度的数组,来存储这些参数。
public class DemoVarArgs {
public static void main(String[] args) {
int p= add(1, 2, 3);
System.out.println(p);
}
//定义计算0-n个整数的和的方法
//add()创建一个长度为0的数组,add(1,2,3)创建一个长度为3的数组存储传递过来的参数
public static int add(int...arr){
int sum = 0;
for(int i : arr){
sum += i;
}
return sum;
}
}
可变参数的注意事项:
- 一个方法的参数列表,只能有一个可变参数。
- 如果方法的可变参数有多个,那么可变参数必须写在参数列表的末尾。
2.Collections集合
java.utils.Collections是集合工具类,用来对集合进行操作。
2.1 addAll&shuffle方法
- public static boolean addAll(Collection c, T… elements):往集合中添加一些元素;
- public static void shuffle(List<?> list):打乱集合顺序。
2.2 sort(List)方法
public static void sort(List list):将集合中的元素按照默认规则排序(默认为升序)。
sort(List list)使用前提: 被排序的集合里存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则。
Comparable接口的排序规则: 自己(this) - 参数 (升序),参数 - 自己(this) (降序)。
import java.util.Objects;
public class Person implements Comparable<Person>{
private int age;
private String name;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public int compareTo(Person o) {
//按年龄升序排序
return this.getAge() - o.getAge();
//return o.getAge() - this.getAge(); //降序排序
}
}
2.3 sort(List, Comparator)方法
public static void sort(List list, Comparator<? super T>):将集合中的元素按照指定规则排序。
Comparator和Comparable的区别:
Comparator:相当于找一个第三方的裁判,比较两个
Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
Comparator的排序规则: o1 - o2升序,o2 - o1降序
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class DemoCollections {
public static void main(String[] args) {
// demo1();
// demo2();
demo3();
}
public static void demo1() {
ArrayList<String> list = new ArrayList<>();
//添加多个元素
Collections.addAll(list,"a","b","c","d","e");
System.out.println(list); //[a, b, c, d, e]
//打乱顺序,每次都不一样
Collections.shuffle(list);
System.out.println(list); //[c, e, b, a, d]
}
public static void demo2() {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","d","e","c","b");
System.out.println(list); //[a, d, e, c, b]
//按升序来排序
Collections.sort(list);
System.out.println(list); //[a, b, c, d, e]
//自定义类型的排序
ArrayList<Person> list2 = new ArrayList<>();
list2.add(new Person(10, "张三"));
list2.add(new Person(8, "李四"));
list2.add(new Person(12, "王五"));
Collections.sort(list2);
System.out.println(list2); //[Person{age=8, name='李四'}, Person{age=10, name='张三'}, Person{age=12, name='王五'}]
}
public static void demo3() {
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1, 5, 4, 2);
Collections.sort(list, new Comparator<Integer>() {
//重写排序规则
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2; //升序
}
});
System.out.println(list); //[1, 2, 4, 5]
//自定义类型的使用
ArrayList<Person> list2 = new ArrayList<>();
list2.add(new Person(10, "张三"));
list2.add(new Person(8, "李四"));
list2.add(new Person(12, "王五"));
Collections.sort(list2, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge(); //按年龄升序排序
}
});
System.out.println(list2);//[Person{age=8, name='李四'}, Person{age=10, name='张三'}, Person{age=12, name='王五'}]
}
}
3.Map接口概述
Collection集合和Map集合的区别:
- Collection中的集合,元素都是孤立存在的,像集合中存储元素采用一个个元素的方式存储。 Map中的集合,元素是成对存在的,每个元素由键和值两部分组成,通过键可以找到对应的值。
- Collection中的集合称为单列集合,Map中的集合称为双列集合。
java.util.Map<K, V>集合,K:此映射所维护的键的类型,Y:所映射值的类型
Map集合的特点:
- Map集合是一个双列集合,一个元素包含两个值(key和value)。
- Map集合中的元素,key和value的数值类型可以相同,也可以不同。
- Map集合中的元素,key是不允许重复的,value是可以重复的。
- Map集合中的元素,key和value是一一对应的。
3.1 Map常用子类
java.util.HashMap<K, V>集合 implements Map<K, V>接口
HashMap集合的特点:
- HashMap集合底层是哈希表,查询的速度特别的快。
- HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致。
java.util.LinkedHashMap<K, V>集合 extends HashMap<K, V>集合
LinkedHashMap集合的特点:
- LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)。
- LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的。
3.2 Map接口中的常用方法
- public V put(K key, V value):把指定的键与指定的值添加到Map集合中。
返回值:V,存储键值对的时候,key不重复,返回值是null;key重复,会使用新的value替换Map中重复的value,返回值为被替换的value。 - public V remove(Object key):把指定的键所对应的键值的元素在Map集合中删除,返回被删除元素的值。
返回值:V,key存在,返回被删除的值;key不存在,返回null。 - public V get(Object key):根据指定的键,在Map集合中获取对应的值。
返回值:V,key存在,返回对应的value值;key不存在,返回null。 - boolean containsKey(Object key):判断集合中是否包含指定的键。
包含返回true,不包含返回flase。
import java.util.HashMap;
import java.util.Map;
public class DemoMap {
public static void main(String[] args) {
show1();
show2();
show3();
show4();
}
public static void show1(){
Map<String, String> map = new HashMap<>();
String s1 = map.put("猪猪侠", "小菲菲1");
System.out.println("s1: " + s1); //s1: null
String s2 = map.put("猪猪侠", "小菲菲2");
System.out.println("s2: " + s2); //s2: 小菲菲1
System.out.println(map); //{猪猪侠=小菲菲2}
}
public static void show2(){
Map<String, Integer> map = new HashMap<>();
map.put("猪猪侠", 120);
map.put("小菲菲", 100);
System.out.println(map); //{小菲菲=100, 猪猪侠=120}
Integer in1 = map.remove("猪猪侠");
System.out.println("in1: " + in1); //in1: 120
Integer in2 = map.remove("光头强");
System.out.println("in2: " + in2); //in2: null
System.out.println(map); //{小菲菲=100}
}
public static void show3(){
Map<String, Integer> map = new HashMap<>();
map.put("猪猪侠", 120);
map.put("小菲菲", 100);
Integer in1 = map.get("猪猪侠");
System.out.println("in1: " + in1); //in1: 120
Integer in2 = map.get("光头强");
System.out.println("in2: " + in2); //in2: null
}
public static void show4(){
Map<String, Integer> map = new HashMap<>();
map.put("猪猪侠", 120);
map.put("小菲菲", 100);
System.out.println(map.containsKey("猪猪侠")); //true
System.out.println(map.containsKey("熊大")); //false
}
}
3.3 Map集合遍历键找值方式
Map集合的第一种遍历方法:通过键找值的方法。
方法: public Set<K> keySet():获取Map集合中所有的键,存储到Set集合中。
实现步骤:
- 使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中;
- 遍历Set集合,获取Map集合中的每一个key;
- 通过Map集合中的方法get(key),通过key找到value。
public class DemoKeySet {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("猪猪侠", 120);
map.put("小菲菲", 100);
map.put("超人强", 140);
Set<String> set = map.keySet();
//使用迭代器遍历
Iterator<String> it = set.iterator();
while(it.hasNext()){
String key = it.next();
Integer value = map.get(key);
System.out.println(key + "=" + value);
}
//使用增强for循环遍历
for(String key : set){
Integer value = map.get(key);
System.out.println(key + "=" + value);
}
}
}
3.4 Entry键值对对象,Map集合遍历键值对方法
Map集合遍历的第二种方法:使用Entry对象遍历。
方法: public Set<Map.Entry<K, V>> entrySet():获取到Map集合中所有的键值对对象的集合(Set集合)。
Map.Entry<K, V>: 在Map接口中有一个内部接口Entry
作用: 当Map集合一创建,那么就会在Map集合中创建一个Entry对象,用来记录键和值(键值对对象,键与值的映射关系)。
实现步骤:
- 使用Map集合中的方法entrySet(),把Map集合内部的多个Entry对象取出来,存储到一个Set集合中;
- 遍历Set集合,获取Set集合中的每一个Entry对象;
- Entry对象中的方法getKey()获取key,getValue()获取value。
public class DemoKeySet {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("猪猪侠", 120);
map.put("小菲菲", 100);
map.put("超人强", 140);
Set<Map.Entry<String, Integer>> set = map.entrySet();
//使用迭代器遍历
Iterator<Map.Entry<String, Integer>> it = set.iterator();
while(it.hasNext()){
Map.Entry<String, Integer> e = it.next();
System.out.println(e.getKey() + "=" + e.getValue());
}
//使用增强for循环遍历
for(Map.Entry<String, Integer> e : set){
System.out.println(e.getKey() + "=" + e.getValue());
}
}
}
3.5 HashMap存储自定义类型键值
Map集合保证key是唯一的:作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一。
public class DemoHashMapSavePerson {
public static void main(String[] args) {
demo1();
demo2();
}
/*key:Integer类重写了hashCode和equals方法,可以保证key唯一*/
public static void demo1() {
HashMap<Integer, Person> map = new HashMap<>();
map.put(1, new Person(12, "猪猪侠"));
map.put(2, new Person(11, "小菲菲"));
map.put(1, new Person(13, "超人强"));
Set<Integer> set = map.keySet();
for(Integer key: set){
Person value = map.get(key);
System.out.println(value);
}
//Person{age=13, name='超人强'}
//Person{age=11, name='小菲菲'}
}
/*key:为保证key唯一,Person类需要重写hashCode和equals方法,*/
public static void demo2() {
HashMap<Person, Integer> map = new HashMap<>();
map.put(new Person(12, "猪猪侠"), 1);
map.put(new Person(11, "小菲菲"), 2);
map.put(new Person(12, "猪猪侠"), 3);
Set<Map.Entry<Person, Integer>> set = map.entrySet();
for(Map.Entry<Person, Integer> entry: set){
Person key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + "=" + value);
}
}
}
public class Person implements Comparable<Person>{
private int age;
private String name;
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
...
}
3.6 LinkedHashMap集合
java.util.LinkeHashMap<K, V>集合 extends HashMap<K, V>集合
底层原理:哈希表 + 链表(记录元素顺序)。
LinkedHashMap集合:key不允许重复,有序
HashMap集合:key不允许重复,无序
3.7 Hashtable集合
java.util.Hashtable<K, V>集合 implements Map<K, V>接口
Hashtable: 底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢,不能存储null值,null键;
HashMap: 底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快,可以存储null值,null键。