一:首先说一下本人对集合的理解,其实就是一个容器,简单说就是Java中用来放数据的一个工具,那么这些到底有哪些
大家看下面这张图
说明了collection接口的所有子类以及它的特点。特点知道之后,你要知道这些接口方法如何存取数据;
特性大家看上面的图即可,下面说一下每种接口方法的如何存取数据。
List接口中带索引的方法(特有)
- public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
- public E get(int index):返回集合中指定位置的元素。
- public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
- public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
注意:
操作索引的时候,一定要防止索引越界异常
IndexOutOfBoundsException:索引越界异常,集合会报
ArrayIndexOutOfBoundsException:数组索引越界异常
StringIndexOutOfBoundsException:字符串索引越界异常
ArrayList集合:是List集合的子类
java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以
ArrayList`是最常用的集合。许多人开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
List集合遍历有3种方式
//使用普通的for循环
for(int i=0; i<list.size(); i++){
//public E get(int index):返回集合中指定位置的元素。
String s = list.get(i);
System.out.println(s);
}
System.out.println("-----------------");
//使用迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("-----------------");
//使用增强for
for (String s : list) {
System.out.println(s);
}
String r = list.get(5);//IndexOutOfBoundsException: Index 5 out-of-bounds for length 5
System.out.println(r);
二: LinkedList集合:java.util.LinkedList集合 implements List接口
LinkedList集合的特点:
1.底层是一个链表结构:查询慢,增删快
2.里边包含了大量操作首尾元素的方法
注意:使用LinkedList集合特有的方法,不能使用多态 写法如下所示
//创建LinkedList集合对象
LinkedList<String> linked = new LinkedList<>();
LinkedList集合的一些方法,具体可阅读jdk api
- public void addFirst(E e):将指定元素插入此列表的开头。
- public void addLast(E e):将指定元素添加到此列表的结尾。
- public void push(E e):将元素推入此列表所表示的堆栈。
- public E getFirst():返回此列表的第一个元素。
- public E getLast():返回此列表的最后一个元素。
- public E removeFirst():移除并返回此列表的第一个元素。
- public E removeLast():移除并返回此列表的最后一个元素。
- public E pop():从此列表所表示的堆栈处弹出一个元素。
- public boolean isEmpty():如果列表不包含元素,则返回true。
java.util.Set接口 extends Collection接口
Set接口的特点:
1.不允许存储重复的元素
2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
java.util.HashSet集合 implements Set接口
HashSet特点:
1.不允许存储重复的元素
2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致
4.底层是一个哈希表结构(查询的速度非常的快)
注意:HashSet的实现原理
HashSet添加与遍历如下
Set<Integer> set = new HashSet<>(); //多态的写法
//使用add方法往集合中添加元素
set.add(1);
set.add(3);
set.add(2);
set.add(1);
//使用迭代器遍历set集合
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
Integer n = it.next();
System.out.println(n);//1,2,3
}
//使用增强for遍历set集合
System.out.println("-----------------");
for (Integer i : set) {
System.out.println(i);
}
三:HashSet存储自定义类型元素
由于 HashSet底层存储的时候是哈希表的形式,首先计算哈希值,如果哈希值一样,则发生哈希冲突,就要链表解决哈希地址冲突的问题,jdk1.8之后,如果冲突的个数超过八个,就用红黑数解决,红黑树趋近平衡树,也是查询非常快。所以这里存储自定义类型元素时,为保证元素的唯一性,在
public static void main(String[] args) {
//创建HashSet集合存储Person
HashSet<Person> set = new HashSet<>();
Person p1 = new Person("小美女",18);
Person p2 = new Person("小美女",18);
Person p3 = new Person("小美女",19);
System.out.println(p1.hashCode());//1967205423
System.out.println(p2.hashCode());//42121758
System.out.println(p1==p2);//false
System.out.println(p1.equals(p2));//false
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set);
}
四:LinkedHashSet——有序,不允许重复:java.util.LinkedHashSet集合 extends HashSet集合,底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序;下面是一个例子
HashSet<String> set = new HashSet<>();
set.add("www");
set.add("abc");
set.add("abc");
set.add("fyj");
System.out.println(set);//[abc, www, itcast] 无序,不允许重复
LinkedHashSet<String> linked = new LinkedHashSet<>();
linked.add("www");
linked.add("abc");
linked.add("abc");
linked.add("fyj");
System.out.println(linked);//[www, abc, itcast] 有序,不允许重复
一:Map集合:java.util.Map<k,v>集合,首先说一下Map和Collection集合下面的List、Set区别,三者都是Java集合框架下的基础接口,但是Map没有继承Collection,而List、Set继承Collection,下面这幅图说明了之间的区别与联系
二:Map集合的特点:
1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
2.Map集合中的元素,key和value的数据类型可以相同,也可以不同
3.Map集合中的元素,key是不允许重复的,value是可以重复的
4.Map集合中的元素,key和value是一一对应
Map接口最重要的两个子接口:HashMap,LinkedHashMap
HashMap,LinkedHashMap连个接口的特点如下:
java.util.HashMap<k,v>集合 implements Map<k,v>接口 HashMap集合的特点:
1.HashMap集合底层是哈希表:查询的速度特别的快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表|红黑树(链表的长度超过8):提高查询的速度
2.hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致 java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
LinkedHashMap的特点:
1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
三:Map几个函数的使用方法
public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。
/*
public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
返回值:
key存在,返回对应的value值
key不存在,返回null
*/
private static void show03() {
//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("范颜军",168);
map.put("王五",165);
map.put("林志玲",178);
Integer v1 = map.get("王五");
System.out.println("v1:"+v1);//v1:165
Integer v2 = map.get("giao");
System.out.println("v2:"+v2);//v2:null
}
/*
public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
返回值:V
key存在,v返回被删除的值
key不存在,v返回null
*/
@Test
void show02() {
//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("范颜军",168);
map.put("王五",165);
map.put("林志玲",178);
map.remove("林志玲");
System.out.println(map);
}
/*
public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。
返回值:v
存储键值对的时候,key不重复,返回值V是null
存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值
*/
private static void show01() {
//创建Map集合对象,多态
Map<String,String> map = new HashMap<>();
String v1 = map.put("范颜军", "jlj1");
System.out.println("v1:"+v1);//v1:null
String v2 = map.put("范颜军", "jlj2");
System.out.println("v2:"+v2);//v2:jll1
System.out.println(map);//{范颜军=jlj2}
map.put("范颜军1","jlj1");
map.put("范颜军2","jlj2");
map.put("范颜军2","jlj3");
System.out.println(map);
四:Map集合的遍历
Map集合的遍历方式:两种
第一种:通过键找值的方式,方法提示keySet()
原理
遍历的方法(keySet() )————比较简单,容易理解,一般会使用这种方式遍历Map集合:
Set keySet() 返回此映射中包含的键的 Set 视图。
实现步骤:
1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
2.遍历set集合,获取Map集合中的每一个key
3.通过Map集合中的方法get(key),通过key找到value
下面是一个例子
public static void main(String[] args) {
//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("fyj",168); //字母表示人名字
map.put("dfs",165);
map.put("jlj",178);
//1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
Set<String> set = map.keySet();
//2.遍历set集合,获取Map集合中的每一个key
//使用迭代器遍历Set集合
Iterator<String> it = set.iterator();
while (it.hasNext()){
String key = it.next();
//3.通过Map集合中的方法get(key),通过key找到value
Integer value = map.get(key);
System.out.println(key+"="+value);
}
System.out.println("-------------------");
//使用增强for遍历Set集合
for(String key : set){
//3.通过Map集合中的方法get(key),通过key找到value
Integer value = map.get(key);
System.out.println(key+"="+value);
}
System.out.println("-------------------");
//使用增强for遍历Set集合
for(String key : map.keySet()){
//3.通过Map集合中的方法get(key),通过key找到value
Integer value = map.get(key);
System.out.println(key+"="+value);
}
}
第二种:使用Entry对象遍历
Entry对象:键值对对象,MapEntry<K,V>:在Map接口中有一个内部接口Entry
作用:当Map集合一创建,那么就会在Map集合中创建一个Entry对象,相当于结婚证(键值都有)
遍历的方法( entrySet() ):
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
实现步骤:
1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
2.遍历Set集合,获取每一个Entry对象
3.使用Entry对象中的方法getKey()和getValue()获取键与值
下面是举例子
public static void main(String[] args) {
//创建Map集合对象
Map<String,Integer> map = new HashMap<>();
map.put("范延钧",168);
map.put("jlj",165);
map.put("fyj",178);
//1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
Set<Map.Entry<String, Integer>> set = map.entrySet();
//2.遍历Set集合,获取每一个Entry对象
//使用迭代器遍历Set集合
Iterator<Map.Entry<String, Integer>> it = set.iterator();
while(it.hasNext()){
Map.Entry<String, Integer> entry = it.next();
//3.使用Entry对象中的方法getKey()和getValue()获取键与值
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key+"="+value);
}
System.out.println("-----------------------");
for(Map.Entry<String,Integer> entry:set){
//3.使用Entry对象中的方法getKey()和getValue()获取键与值
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key+"="+value);
}
}
HashMap存储自定义类型键值
Map集合(自定义的时候)保证key是唯一的,保证方法就是重写hashcode()和equals()方法;
下面举例说明:
首先定义了一个Person类如下
package com.fyj.cn.map.map2;
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", 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;
}
}
第一种存储: key:String类型,由于String类重写hashCode方法和equals方法,可以保证key唯一,所以这里不再需要在Person类里面重写hashCode方法和equals方法,存储测试代码如下:
/*
HashMap存储自定义类型键值
key:String类型
String类重写hashCode方法和equals方法,可以保证key唯一
value:Person类型
value可以重复(同名同年龄的人视为同一个)
*/
@Test
public void show01() {
//创建HashMap集合
HashMap<String,Person> map = new HashMap<>();
//往集合中添加元素
map.put("北京",new Person("张三",18));
map.put("上海",new Person("李四",19));
map.put("广州",new Person("王五",20));
map.put("北京",new Person("赵六",18));
//使用keySet加增强for遍历Map集合
Set<String> set = map.keySet();
for (String key : set) {
Person value = map.get(key);
System.out.println(key+"-->"+value);
}
}
第二种存储: key是Person类型,自定义的,这个时候,Person类就必须重写hashCode方法和equals方法,以保证key唯一,在Person里面重写如下
package com.fyj.cn.map.map2;
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@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(name, 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;
}
}
存储测试代码如下:
/*
HashMap存储自定义类型键值
key:Person类型
Person类就必须重写hashCode方法和equals方法,以保证key唯一
value:String类型
可以重复
*/
@Test
public void show02() {
//创建HashMap集合
HashMap<Person,String> map = new HashMap<>();
//往集合中添加元素
map.put(new Person("女王",18),"英国");
map.put(new Person("秦始皇",18),"秦国");
map.put(new Person("普京",30),"俄罗斯");
map.put(new Person("女王",18),"毛里求斯");
//使用entrySet和增强for遍历Map集合
Set<Map.Entry<Person, String>> set = map.entrySet();
for (Map.Entry<Person, String> entry : set) {
Person key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"-->"+value);
}
五:LinkedHashMap<K,V> entends HashMap<K,V>
底层原理:
哈希表+链表(记录元素的顺序) ,元素有序
二者的比较如下:
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<>();
map.put("a","a");
map.put("c","c");
map.put("b","b");
map.put("a","d");
System.out.println(map);// key不允许重复,无序 {a=d, b=b, c=c}
LinkedHashMap<String,String> linked = new LinkedHashMap<>();
linked.put("a","a");
linked.put("c","c");
linked.put("b","b");
linked.put("a","d");
System.out.println(linked);// key不允许重复,有序 {a=d, c=c, b=b}
}
java.util.Hashtable<K,V>集合 implements Map<K,V>接口
Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快HashMap集合(前面所有的集合):可以存储null值,null键 Hashtable集合,不能存储null值,null键 Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
思考总结:
Map、List、Set三者的区别?
回答:主要是两个方面,元素是否有序,是否允许重复
Map、key不允许重复,value可以重复
List、有序,允许重复
Set、无序,不允许重复(元素唯一),但是他的子接口HashSet的子类LingkedHashSet的底层由于是哈希表+链表,素以保证了元素的有序性,是一个特例
顺带提一下HashMap的put<k,v>底层流程,即存储原理,前面提到的HashSet的底层也是这样实现的
关于map集合的一些常见面试问题以及底层源码的一些说明,我下一篇文章会说明