Java的类集框架很多,也很重要。这里给出的图解可以理解为相应的继承关系,也可以作为重要知识点回顾;
Collection集合接口继承自:Iterable
public interface Collection<E> extends Iterable<E>
java.util.Collection
是单值集合操作的最大父接口,其中有几种核心操作方法和常用操作方法;
Modifier and Type
Method(public)
Description
boolean
add(E e)
确保该集合包含指定元素(可选操作)。
boolean
addAll(Collection<? extends E> c)
在此集中添加指定集合中的所有元素(可选操作)。
void
clear()
从集合中删除所有元素(可选操作)。
boolean
contains(Object o)
如果该集包含指定元素,则返回true。
boolean
remove(Object o)
若存在,则集中删除指定元素的单个实例(可选操作)。
int
size()
回到这个集合中的元素数。
Object[]
toArray()
返回包含此集中所有元素的数组。
Iterator<E>
iterator()
返回迭代器,以迭代集合中的元素。
上述方法有两种特殊的方法cotains
与remove
;都需要equals
只有支持方法,才能删除和查询数据;否则,元素就找不到了。
后面是衍生的子类方法。
List集合最大特点:允许保存重复元素,并在父亲界面上扩展其他方法;
继承关系:
public interface List<E> extends Collection<E>
Modifier and Type
Method(public)
Description
void
add(int index, E element)
Inserts the specified element at the specified position in this list (optional operation).
boolean
add(E e)
Appends the specified element to the end of this list (optional operation).
ListIterator<E>
listIterator()
Returns a list iterator over the elements in this list (in proper sequence).
static <E> List<E>
of()
Returns an unmodifiable list containing zero elements.
default void
forEach(Consumer<? super T> action)
Performs the given action for each element of theIterable
until all elements have been processed or the action throws an exception.
实例:
package Java从入门到项目实战.Java类集框架.List集合;import java.util.List;public class 多数据保存 { public static void main(String[] args) { List<String> all = List.of("xbhg","Hello","World","welcome"); Object[] result = all.toArray(); for (Object t: result) { System.out.println(t); } System.out.println("-分割线-"; all.forEach(System.out::println); ///方法引用部分引用结构方法 }}
ArrayList子类继承结构如下:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
实例化:允许重复元素按添加时的顺序保存和保存;
package Java从入门到项目实战.Java类集框架.List集合;import java.util.ArrayList;import java.util.Arrays;import java.util.List;public class ArrayList实例List { public static void main(String[] args) { List<String> all = new ArrayList<String>(); all.add("Hello"); all.add(“你好”); all.add(“你好”); all.add("xbhog"); System.out.println(all); all.forEach(System.out::print); //lambda表达式 all.forEach((str)->{ System.out.print(str+"、"); }); }}
集合操作方法:
package Java从入门到项目实战.Java类集框架.List集合;import java.util.ArrayList;import java.util.List;public class ArrayLIST集相关操作 { public static void main(String[] args) { List<String> all = new ArrayList<String>(); System.out.println("集合是空的吗?"+all.isEmpty()+"、“集合元素数”+all.size()); all.add("1"); all.add("2"); all.add("3"); System.out.println("集合是空的吗?"+all.isEmpty()+"、“集合元素数”+all.size()); System.out.println(all.get(1)); System.out.println(all.remove("3")); System.out.println("集合是空的吗?"+all.isEmpty()+"、“集合元素数”+all.size()); }}
ArrayList原理分析:重点:
首先要明确ArrayList是通过数组实现的;这就导致了ArrayList通过什么方式进行扩容操作,以及在什么情况下才会扩容?
ArrayList类中的数组是在结构方法中开辟的空间;相应的参与和参与结构方法:
无参与结构方法:使用空数组(长度为0)初始化,第一次使用时为其开辟空间(初始化程度为10);
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}////默认开放空间大小privatete static final int DEFAULT_CAPACITY = 10;/** * Shared empty array instance used for empty instances. */private static final Object[] EMPTY_ELEMENTDATA = {};/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
有参结构方法:如果长度大于0,则以指定长度打开数组空间;如果长度为0,则按无参结构方法进行;如果是负数,则抛出ILLegalargumentexception异常;
public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // defend against c.toArray (incorrectly) not returning Object[] // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; }}
当数组扩展时,以数组复制的形式将旧数组中的数据复制到开放的新数组中;
最大程度如下:
/** * The maximum size of array to allocate (unless necessary). * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrrayList保存自定义对象:该操作必须包括相关的添加、删除和更改;由于实现contains和remove方法需要通过对象比较来完成;因此,我们需要复制equals方法
package Java从入门到项目实战.Java类集框架.List集合;import java.util.ArrayList;import java.util.List;class Person{ private String name; private int age; public Person(String name,int age){ this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public boolean equals(Object obj){ if(this== obj) return true; if(obj == null) return false; if(!(obj instanceof Person)) return false; Person pe = (Person) obj; return this.name.equals(pe.name) && this.age == pe.age; } @Override public String toString() { return "姓名:"+this.name +"、年龄:"+this.age; }}public class ArrayList保存自定义对象 { public static void main(String[] args) { List<Person> std = new ArrayList<Person>(); std.add(new Person("xbhog",1)); std.add(new Person(小明),2); std.add(new Person(“小白”,3)); System.out.println(-删除前的数据内容-”); std.forEach((person)->{ System.out.println(姓名:"+person.getName()+"、年龄:"+person.getAge()); }); System.out.println(-删除后的数据内容-"); std.remove(new Person(“小白”,3)); std.forEach((person)->{ System.out.println(姓名:"+person.getName()+"、年龄:"+person.getAge()); }); System.out.println("-查看数据是否存在-"; System.out.println(std.contains(new Person(“小明”,2))); }}
LinkedList子类:继承结构如下:基于链表形式的实现
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
实现LinkedList的集合操作:
package Java从入门到项目实战.Java类集框架.List集合;import java.util.LinkedList;import java.util.List;public class LinkList链表操作 { public static void main(String[] args) { List<String> all = new LinkedList<String>(); all.add("java"); all.add("python"); all.add("Linux"); System.out.println(all); System.out.println(all.get(2)); System.out.println(all.get(1)); }}
Vector子类:继承结构如下:
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
从继承结构上可以看出,Vector子类的使用方式与ArrayList相同;
package Java从入门到项目实战.Java类集框架.List集合;import java.util.List;import java.util.Vector;public class Vector子类实现List接口 { public static void main(String[] args) { List<String> all = new Vector<String>(); all.add("asda"); all.add(“你好”); all.add("buhao"); System.out.println(all); }}
不同点:
以下是vector操作方法,采用synchronizeded 同步处理;属于线程安全,但效率不如ArrayList高;
vector子类只能在考虑线程并发访问的情况下使用。
public synchronized void copyInto(Object[] anArray)
Set集合主要特点是重复元素不允许保存在内部
继承结构如下:
public interface Set<E> extends Collection<E>
实例化:
package Java从入门到项目实战.Java类集框架.Set集合;import java.util.Set;public class 基本使用set { public static void main(String[] args) { //不能有重复值,有的话会报错 //Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: 世界// Set<String> all = Set.of(“你好”,"xbhog"世界","世界"; Set<String> all = Set.of“你好”,“xbhog","世界"; System.out.println(all); }}
Hashset子类:特点:散列存放,重复元素不允许保存 也就是说:无序存放
继承结构如下:
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
Hashset保存数据:
package Java从入门到项目实战.Java类集框架.Set集合;import java.util.HashSet;import java.util.Iterator;import java.util.Set;public class Hashset保存数据 { //数据采用无序保存的方式,不允许保存重复的数据 public static void main(String[] args) { ///保存类型为String Set<String> all = new HashSet<String>(); all.add(小李); all.add(小红); all.add("小1"); all.add("小2"); all.add(“小花”; all.add(“小花”; System.out.println(all); Iterator<String> iter = all.iterator(); while(iter.hasNext()){ String str = iter.next(); System.out.print(str+"、"); } }}
LinkedHashset子类:JDK1.4加入-解决Hashset无法顺序保存的数据
实现是基于链表保存的数据:增加的顺序是集合的保存顺序,不会保存重复的数据。
package Java从入门到项目实战.Java类集框架.Set集合;import java.util.Iterator;import java.util.LinkedHashSet;import java.util.LinkedList;import java.util.Set;public class 实现LinkHashsetet链 { //基于链表的操作,保存的数据是按顺序保存的 public static void main(String[] args) { Set<String> all = new LinkedHashSet<String>(); all.add(“小李老师”); all.add(“小bai”); all.add(小明); all.add(“小黄”); System.out.println(all); Iterator<String> iter = all.iterator(); while (iter.hasNext()) { String str = iter.next(); System.out.println(str + "、"); } }}
Treset子类:特点:使集中保存的数据有序排列
其继承结构如下:
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
Treset子类继承Abstractset抽象类,实现Navigableset接口(这个接口是排序标准接口,是Set子类)
Treset保存数据:
package Java从入门到项目实战.Java类集框架.Set集合;import java.util.*;public class Treeset子类有序排列 { ///所有保存的数据按从大到小的顺序(字符按字母大小的顺序进行比较) public static void main(String[] args) { Set<String> all = new TreeSet<String>(); all.add("11"); all.add("hello"); all.add("hello"); all.add("world"); all.add("add"); all.add(部署); all.add(“啊哈”; System.out.println(all); }}
优先级:数字排序>字母排序>汉字排序
Treset子类排序分析:有序数据存储时,该类别根据Comparable接口进行排序;需要注意的是,在复制Compareto()方法时,需要比较类中的所有属性;否则,当某些属性相同时,将被误判为同一对象;导致重复元素判断失败;
package Java从入门到项目实战.Java类集框架.Set集合;import java.util.Set;import java.util.TreeSet;class Member implements Comparable<Member>{ private String name; private int age; public Member(String name,int age){ this.name = name; this.age = age; } public String toString(){ return "姓名:"+this.name +"、年龄:"+this.age; } @Override public int compareTo(Member per){ if(this.age < per.age) return -1; else if(this.age > per.age) return 1; ////年龄相同时比较名称 else { return this.name.compareTo(per.name); } }}public class Treset子类排序分析分析 { public static void main(String[] args) { Set<Member> all = new TreeSet<Member>(); all.add(new Member(张三),12)); all.add(new Member(李四,12); all.add(new Member(王五,20); all.add(new Member(王五”,20)); all.forEach(System.out::println); }}
关于compareto的相关描述,可以在源代码下的注释中翻译。
消除重复元素:(非排序集中的重复元素)依靠两种方法:
- Hash码:public int Hashcode();
- 对象比较:public boolean equals(Object obj);
在比较对象的过程中,首先使用hashcode()方法与集合中保存的代码进行匹配和比较;如果代码相同,则使用equals()方法依次比较属性;如果全部相同;它是相同的元素;
package Java从入门到项目实战.Java类集框架.Set集合;import java.util.*;class Person{ private String name; private int age; public Person(String name,int age){ this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Person)) return false; Person person = (Person) o; if (age != person.age) return false; if (name != null ? !name.equals(person.name) : person.name != null) return false; return true; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; }}public class 消除重复元素 { public static void main(String[] args) { Set<Person> all = new HashSet<Person>(); all.add(new Person(张三),19)); all.add(new Person(李四,19); all.add(new Person(王五,20); all.add(new Person(王五,20); all.add(new Person(魏六),66)); all.add(new Person("xbhog",10)); System.out.println(-第一种输入方式-”; all.forEach((person -> { System.out.println(person.getName()+"----"+person.getAge()); })); System.out.println("-第二种输入方式-"; Iterator<Person> iter = all.iterator(); while(iter.hasNext()){ Person per = iter.next(); System.out.println(per.getName() + " "+per.getAge()); } }}
通过hashset 保存重复元素,然后实现去重操作的两种方法。
集合输出集合在类框架中的标准输出为:Iterator
、ListIterator
、Enumeration
、foreach
;
迭代输出:依次判断每个元素,判断它是否有内容,如果有内容就输出。
Iterator接口依靠Iterable接口中的iterate()实例化;
Iterator常用方法:
boolean
hasNext()
Returnstrue
if the iteration has more elements.
E
next()
返回迭代中的下一个元素。
default void
remove()
将迭代器返回的最后一个元素(可选操作)从基本集合中删除。
boolean
hasNext()
如果迭代中有更多的元素,则返回true。
实例:
package Java从入门到项目实战.Java类集框架.集合输出;import java.util.Iterator;import java.util.Set;public class Iterator输出Set集合 { public static void main(String[] args) { Set<String> all = Set.of("Hello","World","xbhog"); Iterator<String> iter = all.iterator(); while(iter.hasNext()){ String str = iter.next(); System.out.print(str+"、"); } }}
数据删除问题:
在collection和iterator中都有remove方法;那么应该选择什么呢?
无论三七二十一,Collection都会给你删除,这会导致Java.util.concurrentmodificationexception错误;
而Iterator在迭代时;需要根据存储的数据内容进行判断;
因此,只有Iterator接口中的remove才是实现数据删除的正确方法。
如:
package cn.mldn.demo;import java.util.HashSet;import java.util.Iterator;import java.util.Set;public class JavaCollectDemo { public static void main(String[] args) { // 假如使用Set.of()或List.of()创建的集合不支持删除操作 Set<String> all = new HashSet<String>(); all.add(小白); // 保存数据 all.add("Java"); // 保存数据 all.add("Java"); // 保存重复数据 all.add("xbhog"); // 保存数据 Iterator<String> iter = all.iterator(); while (iter.hasNext()) { // 集合是否有数据 String str = iter.next(); // 获取数据 if ("Java".equals(str)) { iter.remove() ; // 删除当前数据 } else { System.out.print(str + "、"); } } }}
Listiterator双向迭代:首先区分Iterator的作用:
Iterator从前到后单向输出
Listiterator完成后向前输出
但只有实现Iterator从前到后的输出,才能实现Listiterator从后到前的输出(注意顺序);
因为指针只有在从前到后输出结束后才能执行;
如果顺序相反,则输出为空。
以下扩展方法:
Modifier and Type
Method
Description
boolean
hasPrevious()
如果列表迭代器在反向遍历列表中有更多的元素,则返回true。
E
previous()
返回列表中的前一个元素,并将光标位置向后移动。
实例:实施双向迭代
package Java从入门到项目实战.Java类集框架.集合输出;import java.util.ArrayList;import java.util.List;import java.util.ListIterator;public class Listiteratorator双向迭代输出 { public static void main(String[] args) { List<String> all = new ArrayList<String>(); all.add(小李); all.add(“小宋”); all.add("xbhog"); ListIterator<String> iter = all.listIterator(); System.out.println(”执行从后到前的操作,必须先执行从前到后的操作,只有这样,指针才能指向后面”; //如果不按相关操作进行,从后到前的操作输出为空; System.out.println(从前到后输出:-; while(iter.hasNext()){ System.out.println(iter.next()); } System.out.println(从后到前输出:-; while(iter.hasPrevious()){ ///判断是否有前一个元素 System.out.println(iter.previous()); ///有些元素输出前一个元素 } }}
Listiterator接口实现了List集合的双向迭代操作。
Enumeration枚举输出:这种类型的输出是基于Vector集合;相当于依附产品;
常用的接口方法:
Modifier and Type
Method
Description
boolean
hasMoreElements()
测试此枚举是否含有更多元素。
E
nextElement()
如果枚举对象至少有一个要提供的元素,则返回枚举的下一个元素。
实例:输出vector集合数据
package cn.mldn.demo;import java.util.Enumeration;import java.util.Vector;public class JavaCollectDemo { public static void main(String[] args) { Vector<String> all = new Vector<String>(); // 实例化Vector all.add(“小黄”); // 保存数据 all.add("Java"); // 保存数据 all.add("xbhog"); // 保存数据 Enumeration<String> enu = all.elements() ; // 获得Enumeration实例 while (enu.hasMoreElements()) { String str = enu.nextElement() ; System.out.print(str + "、"); } }}
请注意,Enumeration只有输出操作没有删除操作。
foreach输出:没什么好说的,既能实现数组输出,又能支持集合输出;
package cn.mldn.demo;import java.util.HashSet;import java.util.Set;public class JavaCollectDemo { public static void main(String[] args) { Set<String> all = new HashSet<String>(); // 实例化Set all.add(“小黄”); // 保存数据 all.add("Java"); // 保存数据 all.add("xbhog"); // 保存数据 for (String str : all) { System.out.print(str + "、"); } }}
实现foreach自定义输出:
首先要知道实现foreach需要iterator接口的支持;因此,只有在set和List集合中,才能通过foreach实现输出;
如果要实现自定义输出,则需要实例Iterable接口来完成iterator功能;
package cn.mldn.demo;import java.util.Iterator;class Message implements Iterable<String> {// 支持foreach输出 private String[] content = { "Java", "Python", "Ubuntu" }; // 信息内容 private int foot; // 操作脚标 @Override public Iterator<String> iterator() { // 获得Iterator实例 return new MessageIterator(); } private class MessageIterator implements Iterator<String> { @Override public boolean hasNext() { // 判断内容是否存在 return Message.this.foot < Message.this.content.length; } @Override public String next() { // 获取数据 return Message.this.content[Message.this.foot++]; } }}public class JavaCollectDemo { public static void main(String[] args) { Message message = new Message(); // Iterable接口实例 for (String msg : message) { // foreach输出 System.out.print(msg + "、"); } }}
Map集合map的集合形式是键值对的方式;
常用方法:
Modifier and Type
Method
Description
V
get(Object key)
Returns the value to which the specified key is mapped, ornull
if this map contains no mapping for the key.
V
put(K key, V value)
Associates the specified value with the specified key in this map (optional operation).
static <K,V> Map<K,V>
of()
Returns an unmodifiable map containing zero mappings.
其中Map.of()可将每组数据转换为map进行保存;
使用Map保存Key-Value数据:
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.Collection;import java.util.Map;import java.util.Set;public class 保存Key_Value数据 { public static void main(String[] args) {// 假如Key重复 则会报错:java.lang.IllegalArgumentException// 假如Value和Key设置为null,则会报错:java.lang.NullPointerException Map<String,Integer> map = Map.of("one",1,"two",null); System.out.println(map);// System.out.println(map.get("one"));// Put只能在map子类中使用 }}
注意点:
- 假如Key重复 则会报错:java.lang.IllegalArgumentException
- 假如Value和Key设置为null,则会报错:java.lang.NullPointerException
特点:数据通过散列保存,即无序排列
继承结构:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
Map集合操作HashMap:
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.HashMap;import java.util.Map;public class HahMap子类 { public static void main(String[] args) { Map<String,Integer> map = new HashMap<String,Integer>();// Map集合操作/// map.put("One",1);// map.put("Two",2);// map.put("Three",3);// System.out.println(map);// Map数据的保存方法: System.out.println(map.put("One",1)); //保存数据,但是保存数据的key不存在,返回nulll System.out.println(map.put("One",101)); // 返回1,即返回是覆盖的值 map.put("Three",3); map.put(demo1”,4); map.put(demo2”,null); map.put("Te",6); map.put(null,7); /*结果 * null、7 * */ System.out.println(map.get(demo2); System.out.println(map.get(null)); }}
注意两个输出的注释:
///Map数据保存方法:System.out.println(map.put("One",1)); //保存数据,但保存数据的key不存在,返回nullSystem.out.println(map.put("One",101)); // 返回1,即返回是覆盖的值
Key和Value在使用Map保存数据时不能使用null,但使用Hashmap保存数据可以将Key或Value设置为null,当然也可以使用Key=Value=null,但这种实现保存毫无意义。
put方法可以在发生时返回原始内容,以便根据返回结果判断设置的key是否存在;
Hashmap数据扩展操作原理分析:1) 首先观察结构方法:
设置数据扩展阈值;
public HashMap() { // all other fields defaulted this.loadFactor = DEFAULT_LOAD_FACTOR; }
然后跳转DEFAULT_LOAD_FACTOR
查看:
功能:容量扩展阈值
/** * The load factor used when none specified in constructor. */static final float DEFAULT_LOAD_FACTOR = 0.75f;
通过源码可以发现,每个Hashmap在实例化对象时都考虑到了数据存储的扩展;
2) 在Hashmap中观察put方法
public V put(K key,V value){ return putVal(hash(key),key,value,false,true);}
使用put方法保存数据时,会调用putval方法,同时会对key进行哈希处理(生成hash码)
为了方便数据保存,putval()方法将数据封装为Node节点对象,在putval()方法的操作过程中,将使用reasize()方法进行扩展;
3)容量扩充
当保存的数据超过既定的存储容量时,原则如下:
常量地址:DEFAULT_INITIAL_CAPACITY
;作为一种初始容量配置,然后1向左移动4-16;
常量默认大小为16个元素,即默认可以保存的最大内容为16个元素;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
当保存的数据内容超过设定阈值时DEFAULT_LOAD_FACTOR = 0.75f
相当于容量x阈值 = 16*0.75 = 12;也就是说,当保存到12个元素时,容量会扩大;
扩展模式为两倍扩展:即每次扩展容量的两倍。
4)大数据下的数据存储模式:
JDK1.8后来到大数据时代,引发了Hashmap在大数据量中的访问效率;
它提供了一个重要的常量:TREEIFY_THRESHOLD
static final int TREEIFY_THRESHOLD = 8;
使用Hashmap保存时,如果保存的数据数量不超过阈值8,则以链表的形式存储数据;如果超过此阈值,链表将转换为红黑树,以实现树的平衡;并使用左右旋转来确保数据的查询性能。
LinkedHashmap子类:特点:以链表形式实现偶对存储,可保证存储顺序与数据增加顺序相同;
继承结构:
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
使用LinkedHashMao子类存储数据:
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.LinkedHashMap;import java.util.Map;public class LinkedHashMap子类存储数据 { public static void main(String[] args) { Map<String,Integer> map = new LinkedHashMap<String,Integer>(); map.put(张三,1); map.put(李四); map.put(王五”,3); map.put(null,3); map.put(赵六),null); System.out.println(map); }}
可以发现,集合保存的顺序与数据增加的顺序相同;同时,LinkedHashmap子类允许保存的Key或value内容为null;
Hashtable子类:其继承结构如下:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable
使用Hashtable子类保存数据:
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.LinkedHashMap;import java.util.Map;public class LinkedHashMap子类存储数据 { public static void main(String[] args) { Map<String,Integer> map = new Hashtable<String,Integer>(); map.put(张三,1); map.put(李四); System.out.println(map); }}
Hashmap和Hashtable的区别:
Hashmap中的 方法是异步操作(非线程安全),允许在Hashmap中保存null数据
Hashtable中的方法是同步操作(线程安全),但效率慢,Hashtable不允许保存Null数据;否则,Nullpointexception将出现;
TreeMap子类:特点:Treemap属于有序的Map集合类型;可以按key排序;因此,需要Comaprable接口配合;
继承结构如下:
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
数据Key对TreMap子类进行排序:
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.Map;import java.util.TreeMap;public class TreeMap子类数据Key排序 { public static void main(String[] args) {// 由于该程序保存的Key属于String类型,Comparable接口在String中实现,因此/// 可根据保存的字符编码由低到高进行排序 /* * public final class String implements java.io.Serializable, Comparable<String>, CharSequence * * * */ Map<String,Integer> map = new TreeMap<String,Integer>(); map.put("C",3); map.put("B",2); map.put("A",1); System.out.println(map); }}
Map.Entry内部接口:Map可以在JDK1.9开始时创建Map接口.entry内部接口实例;
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.Map;public class Map_Entry内部接口 { public static void main(String[] args) { Map.Entry<String,Integer> entry = Map.entry("One",1); System.out.println(entry.getKey()); System.out.println(entry.getValue()); //观察使用的子类 System.out.println(entry.getClass().getName()); }}
在程序中继续宁Map.当Entry对象构建时,只有引入Key和Value才会自动使用KeyValueHolder子类实例Map.Entry接口对象。
Iterator输出Map集合:集合数据输出的标准形式是基于Iterator接口;Collection接口直接提供iterator方法,以获得iterator接口实例;但是,由于Map接口中保存的数据是多个Map.Entry接口包装的二元偶对象必须采用Map集合的迭代输出;
- 使Map接口中的entrySet(),Map集合变成Set集合;
- 获得Set接口实例后,可以使用iterator方法获得iterator的实例对象;
- 使用iterator迭代找到每个Map.Entry对象,以及Key和Value的分数。
Iterator和foreach输出Map集合:
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;public class Map集合输出问题 { public static void main(String[] args) { Map<String,Integer> map = new HashMap<String,Integer>(); map.put("One",1); map.put("two",2);// Key和Value在输出map中的值 Set<Map.Entry<String, Integer>> set = map.entrySet();////将Map集合转换为Set集合 Iterator<Map.Entry<String,Integer>> iter = set.iterator(); //获得Iterator接口 while(iter.hasNext(){/////) Set中存的都是Mapp.Entry()对象 Map.Entry<String, Integer> me = iter.next(); System.out.print(me.getKey()+" "+me.getValue()); }// System.out.println("\n"); ///foreach循环输出Map集合: for(Map.Entry<String,Integer> entry:set){ System.out.print(entry.getKey()+" "+entry.getValue()); } }}
自定义Key类型:以自定义的形式实现,但作为Key类型,由于需要数据搜索,必须在类中复制hashcode()和equals()方法。
package Java从入门到项目实战.Java类集框架.Map集合;import java.util.HashMap;import java.util.Map;class Member{ private String name; private int age; public Member(String name,int age){ 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 o) { if (this == o) return true; if (!(o instanceof Member)) return false; Member member = (Member) o; if (age !(o instanceof Member)) return false; Member member = (Member) o; if (age != member.age) return false; if (name != null ? !name.equals(member.name) : member.name != null) return false; return true; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; }}/** Key自定义;作为Key类型,由于数据搜索的需要,因此,HashCode()应该在类中复制 equals();* */public class 定制Key的值 { public static void main(String[] args) { Map<Member,String> map = new HashMap<Member,String>(); map.put(new Member(张三,22),xbhog"); map.put(new Member(李四,23),"博客"); map.put(new Member(王五,26),welcome"); System.out.println(map.get(new Member(张三,22)); }}
key在存储大量数据时可能会出现重复问题,称为Hash冲突;
解决方案:
链地址法(拉链法)、开放寻址,再哈希,建立公共溢出区;