目录
集合类的特点:提供一种存储空间的存储模型,存储的数据容量可以随时发生改变
集合与数组区别
数组:
长度开始时必须指定,而且一旦指定,不能修改
保存的必须为同一类型的元素
使用数组进行增加/删除元素比较麻烦
集合:
可以动态保存任意多个对象,使用比较方便
提供了一系列方便操作对象的方法: add、remove、set、get
使用集合添加,删除新元素的代码简洁明了
集合的框架体系
单列集合
双列集合
解读:
1。集合主要是两组(单列集合,双列集合)
2.Collection接口有两个重要的子接口 List Set,他们的实现子类都是单列集合
3. Map接口的实现子类是双列集合,存放的K-V
Collection
Collection接口实现类的特点
public interface Collection<E> extends Iterable<E>
特点:
1.collection实现子类可以存放多个元素,每个元素可以是Object
2.有些Collection的实现类,可以存放重复的元素,有些不可以
3.有些Collection的实现类,有些是有序的(List),有些不是有序的(Set)
有序:指存取顺序一致
4.Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
Collection接口和常用方法
Collection接口常用方法,以实现子类ArrayList来演示. collectionMethod.java
1) add:添加单个元素
2) remove:删除指定元素
3) contains:查找元素是否存在
4) size:获取元素个数
5) isEmpty:判断是否为空
6) clear:清空
7)addAll:添加多个元素
8) containsAl:查找多个元素是否都存在
9) removeAll:删除多个元素
10)说明:以ArrayList实现类来演示.
import java.sql.Array;
import java.util.ArrayList;
import java.util.List;
public class CollectionMethod {
public static void main(String[] args) {
List list = new ArrayList();
// 1) add:添加单个元素
list.add("Jack");
list.add(10);//list.add(new Integer(10))
list.add(true);
System.out.println("list="+list);
// list.remove(0); 删除第一个元素`
list.remove(true);//删除指定的元素
System.out.println("list="+list);
// 3) contains:查找元素是否存在
System.out.println(list.contains("Jack"));//查找元素是否存在
//4) size:获取元素个数
System.out.println(list.size());
//5) isEmpty:判断是否为空
System.out.println(list.isEmpty());
//6) clear:清空
list.clear();
System.out.println("list="+list);
//7)addAll:添加多个元素
ArrayList list2 = new ArrayList();
list2.add("红楼梦");
list2.add("三国演义");
list.addAll(list2);
System.out.println("list="+list);
//8) containsAl:查找多个元素是否都存在
System.out.println(list.containsAll(list2));
//9) removeAll:删除多个元素
list.removeAll(list2);
System.out.println("list="+list);
}
}
输出结果为:
list=[Jack, 10, true]
list=[Jack, 10]
true
2
false
list=[]
list=[红楼梦, 三国演义]
true
list=[]
使用迭代器遍历
基本介绍
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
Iterator 的结构
Iterator 仅用于遍历集合,Iterator 本身并不存放对象。
迭代器的执行原理
lterator iterator = coll.iterator();//得到一个集合的迭代器
//hasNext():判断是否还有下一个元素
while(iterator.hasNext()){
//next():①指针下移②将下移以后集合位置上的元素返回
System.out.println(iterator.next0));}
iterator接口方法
注意:
在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。
import java.awt.print.Book;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionIterator {
public static void main(String[] args) {
@SuppressWarnings({"all"})//警告抑制
Collection col = new ArrayList();
col.add(new book("三国演义","罗贯中",20.2));
col.add(new book("红楼梦","曹雪芹",150.3));
col.add(new book("小李飞刀","古龙",25.3));
//System.out.println("col="+col);
//遍历col集合
//1,得到集合对应的迭代器
Iterator iterator = col.iterator();
//2,使用while循环遍历
while (iterator.hasNext()){//判断是否还有数据
//返回下一个元素,类型是Object
Object obj = iterator.next();//编译类型是Object,运行类型取决于对象实际类型
System.out.println("obj=" + obj);
}
//快捷键,快速生成while循环 ==> itit
//Ctrl + J 显示所有快捷键
// while (iterator.hasNext()) {
// Object next = iterator.next();
// }
//3. 当退出while循环后,这是iterator迭代器,指向最后的一个元素
//iterator.next();//NoSuchElementException
//4.如果希望再次遍历,
iterator = col.iterator();
System.out.println("第二次遍历");
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println("obj=" + next);
}
}
}
class book {
public String name;
public String author;
public double price;
public book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
List接口方法
List接口是 Collection 接口的子接口
List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复。
List集合中的每个元素都有其对应的顺序索引,即支持索引。
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
List接口常用的实现类有很多,常用的有:ArrayList、LinkedList 和 Vector。
import java.util.ArrayList;
import java.util.List;
public class Demo01 {
public static void main(String[] args) {
//List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复。
List list = new ArrayList();
list.add( "jack");
list.add( "tom");
list.add( "mary");
list.add("hsp");
list.add("tom ");
System.out.println("list=" + list);
//2. List集合中的每个元素都有其对应的顺序索引,即支持索引//索引是从@开始的
System.out.println(list.get(3));//hsp
}
}
List 集合里添加了一些根据索引来操作集合元素的方法
1) void add(int index, Object ele):在index位置插入ele元素
2) boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
3) Object get(int index):获取指定index位置的元素
4)int indexOf(Object obj):返回obj在集合中首次出现的位置
5) int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
6) object remove(int index):移除指定index位置的元素,并返回此元素
7) Object set(int index,Object ele):设置指定index位置的元素为ele相当于是替换.
8) List subList(int fromlndex, int tolndex):返回从fromIndex到tolndex位置的子集合
import java.util.ArrayList;
import java.util.List;
public class Demo02 {
public static void main(String[] args) {
//1) void add(int index, Object ele):在index位置插入ele元素
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
//在index=1的位置插入一个对象
list.add(1,"韩顺平");
System.out.println("list="+list);
//2) boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
List list2 = new ArrayList();
list2.add( "jack");
list2.add( "tom");
list.addAll(1,list2);
System.out.println("list="+list);
//3) Object get(int index):获取指定index位置的元素
System.out.println(list.get(0));
//4)int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom"));
//5) int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
list.add("韩顺平");
System.out.println("list="+list);
System.out.println(list.lastIndexOf("韩顺平"));
//6) object remove(int index):移除指定index位置的元素,并返回此元素
list.remove(0);
System.out.println("list="+list);
//7) Object set(int index,Object ele):设置指定index位置的元素为ele相当于是替换.
list.set(1,"玛丽");
System.out.println("list="+list);
//8) List subList(int fromlndex, int tolndex):返回从fromIndex到tolndex位置的子集合
List returnlist = list.subList(0, 2);
System.out.println("returnlist"+returnlist);
}
}
List三种遍历方式
三种遍历方式,List下所有的实现类都可以用这三种方式遍历元素。
1.使用iterator
lterator iter = col.iterator();
while(iter.hasNext(0){
object o = iter.next();
}
2.使用增强for
for(Object o:col){}
3.使用普通for
for(int i=0;i<list.size();i++){
Object object = list.get(0);
System.out.println(object);
public class ListFor {
public static void main(String[] args) {
//List list = new ArrayList();
//List list = new Vector();
List list = new LinkedList();
list.add("jack");
list.add("tom");
list.add("鱼香肉丝");
list.add("北京烤鸭");
//遍历
//1 . 迭代器
System.out.println("===迭代器===");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//2 . 增强for循环
System.out.println("===增强for===");
// list.forEach(System.out::println);
for (Object o : list) {
System.out.println("o+"+o);
}
//3 . 普通for
System.out.println("===普通for===");
for (int i = 0; i < list.size(); i++) {
System.out.println("对象="+list.get(i));
}
}
}
ArrayList 注意事项
1.ArrayList 可以加入null,并且多个
2.ArrayList是由数组来实现的
3.ArrayList基本等同Vector,除了 ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用ArrrayList。
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class ArrayListDetail {
public static void main(String[] args) {
//ArrayList是线程不安全的,可以看源码 没有 synchronized
/*
public boolean add(E e) {
ensureCapacityInternal(size + 1);// Increments modCount!
elementData[size++] = e;
return true;
}
*/
ArrayList arrayList = new ArrayList();
arrayList.add(null);
arrayList.add("jack");
arrayList.add(null);
System.out.println(arrayList);
}
}
ArrayList底层结构和源码分析
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class ArrayListSource {
public static void main(String[] args) {
// 源码分析
// 使用无参构造器创建ArrayList对象
ArrayList list = new ArrayList();
// 使用for循环给list集合添加 1-10 数据
for (int i = 0; i <= 10; i++) {
list.add(i);
}
// 使用for循环给list集合添加 11-15 数据
for (int i = 11; i<=15;i++){
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
for (Object o : list) {
System.out.println(o);
}
}
}
无参构造器源码分析
有参构造器
List实现类–Vector
1) Vector类的定义说明
public class vector<E>
extends AbstractList<E>
implements List<E>,RandomAccess,cloneable,Serializable
2) Vector底层也是一个对象数组,protected Object[ ]、elementData;
3) Vector是线程同步的,即线程安全, Vector类的操作方法带有synchronized
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArraylndexOutOfBoundsException(index);return elementData(index);
}
4)在开发中,需要线程同步安全时,考虑使用Vector
源码分析
import java.util.Vector;
public class ListVector {
public static void main(String[] args) {
//无参构造器
Vector vector = new Vector();
Vector vector1 = new Vector(10);
//1. new Vector()底层
/*
无参构造:
public Vector() {
this(10);
}
有参构造:
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
*/
for (int i = 0; i < 10; i++) {
vector.add(i);
}
//2. vector.add()
/*
2.1 add方法添加数据到vector集合
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
2.2 确定是否需要扩容,判断条件: if (minCapacity - elementData.length > 0)
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
*/
vector.add(100);
//3.需要扩容的vector 两倍扩容
// capacityIncrement:指定扩容大小,默认为0
//扩容算法:int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
/*
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
*/
}
}
Vector与ArrayList的比较
Vector:
线程安全(同步)效率:安全,效率不高
扩容倍数:如果有参构造1.5倍,如果是无参,那么为第一次10,从第二次开始按1.5倍扩容
ArrayList:
线程安全(同步)效率:不安全,效率高
扩容倍数:如果是无参,默认10,满后,就按2倍扩容。如果指定大小,则每次直接按2倍扩容
List实现类-LinkedList
LinkedList基本介绍
1.LinkedList底层实现了双向链表和双端队列特点
2.可以添加任意元素(元素可以重复),包括null
3.线程不安全,没有实现同步
LinkedList底层结构
1.LinkedList 底层维护了一个双向链表
2.LinkedList中维护了两个属性 first 和 last 分别指向 首节点和尾节点
3.每个节点(Node对象),里面又维护了prev、next、item、三个属性,其中通过prev指向前一个通过next指向后一个节点。最终实现双向链表。
4.LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
mport java.util.Iterator;
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();st="+linkedList);
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
System.out.println("linkedList="+linkedList);
linkedList.remove();
System.out.println("linkedList="+linkedList);
Object set = linkedList.set(0, 666);
System.out.println(set);
System.out.println("linklist="+linkedList);
Object o = linkedList.get(1);
System.out.println(o);
System.out.println("====linkedList迭代器遍历");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
System.out.println("====增强for循环遍历");
for (Object o1 : linkedList) {
System.out.println(o1);
}
System.out.println("====传统for循环");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
}
}
输出结果为:
linkedList=[1, 2, 3]
linkedList=[2, 3]
2
linklist=[666, 3]
3
====linkedList迭代器遍历
666
3
====增强for循环遍历
666
3
====传统for循环
666
3
ArrayList和LinkedList比较
底层结构 | 增删的效率 | 改查的效率 | |
ArrayList | 可变数组 | 较低 数组扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表追加. | 较低 |
如何选择ArrayList和LinkedList:
1)如果我们改查的操作多,选择ArrayList
2)如果我们增删的操作多,选择LinkedList
3)一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
4)在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另
外一个模块是LinkedList,也就是说,要根据业务来进行选择
Set接口方法
Set接口基本介绍
1.无序(添加和取出的顺序不一致),没有索引
2.不允许重复元素,所以最多包含一个null
3.JDK API中Set接口的实现类有
Set接口的常用方法
和List接口一样, Set接口也是Collection的子接口,因此,常用方法和Collection接口一样。
Set接口的遍历方式
同Collection的遍历方法一样,因为set接口是Collection接口的子接口。
1.可以使用迭代器
2.增强for
3.不能使用索引的方式遍历
Set接口常用方法
Method and Description | Modifier and Type |
size() | int |
isEmpty() | boolean |
contains() | boolean |
iterator() | lterator<E> |
toArray() | object[] |
toArray(T[]) | T[] |
add(E) | boolean |
remove(Object) | boolean |
containsAll(Collection<?>) | boolean |
addAll(Collection<? extends E>) | boolean |
retainAll(Collection<?>) | boolean |
removeAll(Collection<?>) | boolean |
clear() | void |
equals(Object) | boolean |
hashCode() | int |
spliterator() | Spliterator<E> |
Set接口遍历方式
1.以Set 接口的实现类 HashSet 来讲解Set接口的方法
2. set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
3.set接口对象存放数据是无序的(添加的顺序和取出的顺序不一致
4.取出的顺序虽然不是添加的顺序,但是它是固定的
@SuppressWarnings({"all"})
public class SetMethod {
public static void main(String[] args) {
Set set = new HashSet();
set.add( "john");
set.add("lucy" );
set.add( "john");//重复
set.add( "jack");
set.add(null);
set.add(null);//再次添加null
for (int i = 0; i < 5; i++) {
System.out.println("set = "+set);
}
//遍历
//方法1:使用迭代器
System.out.println("======使用迭代器======");
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
System.out.println("obj=" +obj);
}
set.remove(null);
//方法2:增强for
System.out.println("========使用增强for=====");
for (Object o :set){
System.out.println("o="+o);
}
//set接口对象 不能通过索引来获取
}
}
Set实现类–HashSet
1.HashSet 实现了Set接口
2.HashSet 实际上是HashMap(源码如下)
public HashSet() {
map = new HashMap<>();
}
3.只能存放一个null值
4.HashSet不保证元素是有序的,取决于hash值,再确定索引的结果
5.不能有重复元素/对象
HashSet set = new HashSet();
//1.在执行add方法后,会返回一个boolean值
//2.如果添加成功,返回true,否则返回false
//3.可以通过remove指定删除哪个对象
System.out.println(set.add("Tom")); // true
System.out.println(set.add("Mike")); // true
System.out.println(set.add("john")); // true
System.out.println(set.add("Tom")); // false
System.out.println(set.add("Jerry")); //true
set.remove("Tom");
System.out.println(set);//[Mike, john, Jerry]
set = new HashSet();
System.out.println("set="+set);
//hashSet 不能添加相同的元素数据
set.add("lucy"); // true
set.add("lucy"); // false
set.add(new Dog("tom")); //true
set.add(new Dog("tom")); //true
System.out.println(set);
System.out.println(set.add(new String("kobe")));//true
System.out.println(set.add(new String("kobe")));//false
}
}
class Dog{//定义Dog类
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
Set实现类–LinkedHashSet
基本介绍
LinkedHashSet 是 HashSet的子类
LinkedHashSet 底层是一个LinkedHashMap,底层维护了一个 数组 + 双向链表
LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存得。
LinkedHashSet 不允许添加重复元素
在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet 有 head和tail)
每一个节点有 before和after属性,这样可以形成双向链表
在添加一个元素时,先求hash值,在求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加【原则和hashset一样】)
这样遍历LinkedHashSet 也能确保插入顺序和遍历顺序一致
代码示例
public class LinkedHashSet_ {
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet();
set.add(new String("AA"));
set.add(456);
set.add(456);
set.add(new Customer("刘",1001));
set.add(123);
set.add("kobe");
for (int i = 0; i < 3; i++) {
set.add(new Customer("kobe",24));
}
System.out.println(set);
}
}
class Customer{
private String name;
private int no;
public Customer(String name, int no) {
this.name = name;
this.no = no;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", no=" + no +
'}';
}
@Override
public int hashCode() {
return 1;
}
}
Set实现类–TreeSet
基本介绍
不允许添加重复元素,不允许添加 null
无序(没有按照输入顺序进行输出)
遍历结果有顺序
底层为排序二叉树(红黑树),且采用中序遍历得到结果 (左节点,根节点,右节点)
代码示例
public class TreeSet_ {
public static void main(String[] args) {
//TreeSet treeSet = new TreeSet();
//使用匿名内部类实现Comparator接口,并重写compare方法,指定排序方法
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length() - ((String)o2).length();
}
});
//添加数据
treeSet.add("jack");
treeSet.add("tom");
treeSet.add("sp");
treeSet.add("abc");//tom 和 abc 长度相等,key相等,添加失败
System.out.println(treeSet);//[sp, tom, jack]
treeSet = new TreeSet();
treeSet.add(new C(1));
treeSet.add(new C(2));
treeSet.add(new C(1));
System.out.println(treeSet);
}
}
class C implements Comparable<C>{
int num;
public C(int num) {
this.num = num;
}
public int compareTo( C c) {
return this.num - c.num;
}
@Override
public String toString() {
return "C{" +
"num=" + num +
'}';
}
}
Map接口
Map体系的继承图
特点:
1.Map用于保存具有映射关系的数据:key-value(双列元素)
2.Map中的 key 和 value 可以是任何引用类型的数据,会封装到 HashMap$Node对象中
3.Map 中的 Key 不允许重复,原因和HashSet 一样,当有相同的key时,等价于替换
4.Map 中的 value 可以重复
5.Map 的key 可以为 null,value 也可以为null,注意 key 为null的只能有一个。
6.常用String类作为Map 的key,因为String类重写了HashCode()和equals().具体情况具体分析可以使用任意类型。
7.key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value
8.Map存放数据的 key-value 示意图,一对 k-v 是放在一个Node中的,又因为Node实现了 Entry接口,有些书上也说,一对k-v是一个Entry
代码示例
public class Map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1","小贤");
map.put("no2","小芸");
map.put("no1","g");
map.put("no3","g");
map.put(null, null);//OK
map.put(null,"456");//等价于替换
map.put("no4",null);//OK
map.put(1,"张三");
map.put(new Object(),"李四");
System.out.println("map="+map);
System.out.println(map.get("no3"));
}
}
Map中的EntrySet
帮助理解Map的第8个特点(Map中只有Entry内部类,EntrySet 、KeySet、Values是其实现类HashMap中的内部类):
1.k-v 最后是 HashMap$Node node = newNode(hash, key, value, null);
2.k-v 为了方便程序员的遍历,还会创建EntrySet 集合,该集合存放的元素的类型 Entry,而一个3.Entry对象就有k,v 即: transient Set<Map.Entry<K,V>> entrySet;
4.在entrySet中,定义的类型是Map.Entry ,但是实际上存放的还是 HashMap$Node,这是因为 HashMap$Node implements Map.Entry
5.这样当把HashMap$Node 对象存放在 EntrySet ,就方便我们的遍历 ,因为Map.Entry提供了重要方法K getKey(); V getValue();,Node对象没有这两个方法。
Map接口常用方法
1.put:添加
2.remove:根据键删除映射关系
3.get:根据键获取值
4.size:获取元素个数
5.isEmpty:判断个数是否为0
6.clear:清除
7.containsKey:查找键是否存在
代码示例:
public class MapMethod {
public static void main(String[] args) {
Map map = new HashMap();
map.put("xian", "芸");
map.put("no1","RNG");
map.put("空",null);
map.put(null,"yy");
map.put(null,null);
System.out.println(map);
map.remove(null);
System.out.println(map);
Object val = map.get("no1");
System.out.println("val="+val);
System.out.println("k-v="+map.size());
System.out.println(map.isEmpty());
map.clear();
System.out.println("map="+map);
map.put("xian", "芸");
map.put("no1","RNG");
map.put("空",null);
map.put(null,"yy");
map.put(null,null);
System.out.println(map.containsKey("xian"));
}
}
遍历方式
共有六种,取k-v对有四种,取值有两种
//1.先取出所有的key,通过key取key-value
Set keySet = map.keySet();
//1.1 增强for循环
System.out.println("========第一种方式========");
for (Object key : keySet) {
System.out.println(key+"-"+map.get(key));
}
//1.2 迭代器
System.out.println("========第二种方式==========");
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key+"-"+map.get(key));
}
//2. 把所有的values取出
Collection values = map.values();
//2.1 增强for
System.out.println("=========第三种方式============");
values.forEach(System.out::println);
//2.2 迭代器
System.out.println("============第四种方式===========");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object next = iterator1.next();
System.out.println(next);
}
//3. 通过EntrySet来获取k-v
Set entrySet = map.entrySet();
//3.1 增强for
System.out.println("===========第五种方式============");
for (Object entry : entrySet) {
//转换为Map.Entry对象
Map.Entry entry1 = (Map.Entry) entry;
System.out.println(entry1.getKey()+"-"+entry1.getValue());
}
//3.2 迭代器
System.out.println("=========第六种方式=========");
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object next = iterator2.next();
System.out.println(next.getClass());//class java.util.HashMap$Node
Map.Entry entry1 = (Map.Entry) next;
System.out.println(entry1.getKey()+"-"+entry1.getValue());
}
HashMap
特点:
1.Map接口的常用实现类:HashMap、Hashtable和Properties
2.HashMap是Map接口使用频率最高的实现类。
3.HashMap是以key-value对的方式来存储数据(HashMap$Node类型)。
4.Key不能重复,但是值可以重复,允许使用null键和null值
5.如果添加相同的key,则会覆盖原来的key-value,等同于修改(key不会替换,val会替换)
6.与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的。(数组+链表+红黑树)
7.HashMap没有实现同步,因此是线程不安全的,方法上没有做同步互斥操作,没有synchronized
底层机制
1.(k,v)是一个Node实现了Map.Entry<K,V>,查看 HashMap的源码可以看到
2.jdk7.0的hashmap底层实现[数组+链表], jdk8.0底层[数组+链表+红黑树
扩容机制
与HashSet扩容机制相同
1.HashMap底层维护了Node类型的数组table,默认为null
2.当创建对象时,将加载因子(loadfactor)初始化为0.75
3.当添加 key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没4.有元素直接添加。如果该索引处有元素,继续判断该元素的key是否和准备加入的key相同,如果相等,则直接替换value;如果不相等需要判断是树结构还是链表,做出相应处理。如果添加时发现容量不够,则需扩容。
5.第一次添加,则需要扩容table容量为16,临界值(threshold)为12 (16 *0.75)
6.以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的2倍,即24,依次类推
7.在Java8中,如果一条链表的元素个数超过 TREEIFY_THRESHOLD(默认是8),并且table的大小 >= MIN_TREEIEF_CAPACITY(默认是64),就会进行树化(红黑树)
HashTable
基本介绍
存放的元素是键值对:K-V
Hashtable的键和值都不能为null,否则会抛出NullPointerException
Hashtable使用方法基本上和 hashMap一样
Hashtable是线程安全的(synchorized),hashMap 是线程不安全的
Hashtable table = new Hashtable();
table.put("john",100);
//table.put(null,100); //NullPointerException
//table.put("join",null); //NullPointerException
table.put("lucy",100);
table.put("lic",100);
table.put("lic",80); // 替换
table.put("hello1",1);
table.put("hello2",1);
table.put("hello3",1);
table.put("hello4",1);
table.put("hello5",1);
table.put("hello6",1);
table.put("hello7",1);
System.out.println(table);
/*
Hashtable底层:
1. 底层有一个数组 Hashtable$Entry[] 初始化大小为11
2. 临界值 threshold 8 = 11 * 0.75
3. 扩容:按照自己的扩容机制运行
4. put()中发现无相同元素时,执行 addEntry(hash, key, value, index); 添加 k-v 封装到 Entry
5. 当 if(count >= threshold) 满足时,没加之前table的count,就进行扩容
6. 按照 int newCapacity = (oldCapaticy << 1 ) + 1; 即[old*2+1]的大小扩容
*/
————————————————
版权声明:本文为CSDN博主「Shane_Cheng0202」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Shane_Cheng0202/article/details/123338648
Properties
基本介绍
Properties类继承HashTable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
他的使用特点和Hashtable类似
Properties 还可以从 xxx.properties 文件中,加载数据到Properties类对象,并进行读取和修改
说明:xxx.properties 文件通常作为配置文件
Properties properties = new Properties();
properties.put("john",100);
// properties.put(null,100); //NullPointerException
// properties.put("john",null); //NullPointerException
properties.put("lucy",100);
properties.put("lic",100);
properties.put("lic",88);
System.out.println(properties);
// 通过 key 获取对应的值
System.out.println(properties.get("lic"));
// 删除
properties.remove("lic");
System.out.println(properties);
// 修改
properties.put("john","约翰");
System.out.println(properties);
TreeMap
基本介绍
唯一
有序(按照升序或降序排列)
底层:二叉树,key遵照二叉树特点
key对应的类型内部一定要实现比较器(内部比较器,外部比较器自选)
代码示例:
public class TreeMap_ {
public static void main(String[] args) {
// 使用默认构造器,创建TreeMap
// TreeMap treeMap = new TreeMap();
//
TreeMap treeMap = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// 按照传入的 k(String) 的大小进行排序
// return ((String) o2).compareTo((String) o1);
// 按照字符串长度大小排序
return ((String) o1).length() - ((String) o2).length();
}
});
treeMap.put("jack","杰克");
treeMap.put("tom","汤姆");
treeMap.put("wkliul","拉拉");
treeMap.put("smith","史密斯");
treeMap.put("smith","123");
System.out.println(treeMap.size());
System.out.println(treeMap);
}
}
如何选择集合实现类
在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择,分析如下:
1)先判断存储的类型(一组对象[单列]或一组键值对[双列)
2)一组对象[单列]:Collection接口
允许重复:List
增删多:LinkedList [底层维护了一个双向链表]
改查多:ArrayList [底层维护Object类型的可变数组]
不允许重复: Set
无序:HashSet[底层是HashMap,维护了一个哈希表即(数组+链表+红黑树)]
排序:TreeSet
插入和取出顺序一致: LinkedHashSet,维护数组+双向链表
3)一组键值对: Map
键无序: HashMap [底层是:哈希表 jdk7: 数组+链表,jdk8:数组+链表+红黑树]
键排序:TreeMap
健插入和取出顺序一致:LinkedHashMap
读取文件:Properties