【博客大赛】Java类集框架详细汇总-底层分析
前言:
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 the Iterable 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; }
//默认的开辟空间大小 private 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;
ArrayList保存自定义类对象:
该操作必然包含了相关的增删改查;由于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操作方法,采用的方式是synchronized 同步处理;属于线程安全,但是效率没有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 链表实现LinkHashSet { //基于链表的操作,且保存的数据为顺序保存 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 + "、"); } } }
TreeSet子类:
特点:使得集合中保存的数据进行有序排列
其继承结构如下:
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
TreeSet子类继承AbstractSet抽象类并实现了NavigableSet接口(该接口为排序标准接口,是Set的子类)
TreeSet保存数据:
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); } }
优先级:数字排序>字母排序>汉字排序
TreeSet子类排序分析:
该类子在进行有序数据存储时依据的是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 TreeSet子类排序分析 { 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迭代输出:
迭代输出:依次判断每个元素,判断其是否有内容,如果有内容就输出。
Iterator接口依靠Iterable接口中的iterate()方法实例化的;
Iterator常用方法:
boolean | hasNext() | Returns true 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 双向迭代输出ListIterator { 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, or null 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
HashMap子类:
特点:采用散列方式保存数据,即无序排列
继承结构:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
HashMap进行map集合的操作:
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不存在,返回null 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不存在,返回null System.out.println(map.put("One",101)); // 返回1,也就是返回的是覆盖掉的值
在使用Map保存数据的时候Key与Value都不能使用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个元素的时候就会进行容量扩充;
扩充的模式是2倍扩充:即每一次扩充2倍的容量。
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("李四",2); 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("李四",2); 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
TreeMap子类进行数据Key的排序:
package Java从入门到项目实战.Java类集框架.Map集合; import java.util.Map; import java.util.TreeMap; public class TreeMap子类进行数据Key的排序 { public static void main(String[] args) { // 因为该程序保存的Key属于String类型,由于String中实现了Comparable接口,所以 // 可以根据保存的字符的编码进行由低到高的进行排序 /* * 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内部接口:
在JDK1.9开始可以利用Map接口中创建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); // 输出的map中的Key与Value的值 Set<Map.Entry<String, Integer>> set = map.entrySet();//将Map集合转变为Set集合 Iterator<Map.Entry<String,Integer>> iter = set.iterator(); //获取Iterator接口 while(iter.hasNext()){ // Set中存的都是Map.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 != 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冲突;
解决的方式:
链地址法(拉链法)、开放寻址、再哈希、建立公共溢出区;

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
推荐一款开源的Diffy自动化测试框架
一款开源的Diffy自动化测试利器实战教程! 1. 前言 软件测试是软件开发生命周期一个十分重要的环节,测试工作开展的好坏,很大程度上决定了产品质量的好坏,但软件产品随着版本的持续迭代,功能日益增多,系统愈加复杂,而从质量保障的角度,除了要保障好每次新增、优化的产品质量外,还需要确认新增或修改的功能不影响之前已存在的功能。若要进行产品功能全量回归,这个测试的工作量将会非常巨大。同时因为是回归,可能几百甚至上千用例中才会发现一个问题,甚至一个问题也没有,测试投入工作的时间与最终的收益不成比例。 因此如何在有限的时间、人力投入下,有效、高效的保证产品回归测试的质量,也一度成为了行业老司机以及团队管理者头疼的问题! 而今天的主角Diffy则为上述问题提供了较好的解决方案。它基于稳定版本和它副本的输出,对候选版本的输出进行严格对比,以检查候选版本是否正确,大大降低了回归工作量。 接下来,让我们详细了解一下Diffy的工作原理,以及结合实战演练带大家感受一下它的魅力。 2. 关于Diffy 关于Diffy,公号此前发表过一篇文章:推荐一款Diffy:Twitter的开源自动化测试工具有过详细介绍...
- 下一篇
做个开源博客学习Vite2 + Vue3 (一)搭建项目
前言 不会 webpack,遇到报错就一头雾水,完全不知道怎么办,而且体积还大速度还慢。所以尤雨溪做了 vite 后就很向往,只是知道自己水平有限还是等大佬先趟趟坑,等差不多了在跳。 现在vite2发布了,官网也有了,网上也能找到大佬写的相关资料,时机应该成熟可以往里跳了。所以打算做个开源博客,一边做这个小项目,一边学习 vite2 和vue3,还有相关知识。 为啥选择博客呢?因为功能相对比较简单,不考虑皮肤等功能的话。而且大家都比较熟悉,没写过博客总看过博文吧。所以呢,业务需求方面一说大家也都懂,不用做过多的解释。 建立项目 因为GitHub总是连不上,速度还慢,所以选择了gitee。先在gitee上面建立一个项目:https://gitee.com/naturefw/vue3-blog 然后clone到本地,接下来就是用vite2建立项目了。 按照官网(https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project)介绍建立项目。 yarncreate@vitejs/app 这里建议使用 yarn,因为可以更快一些。...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7设置SWAP分区,小内存服务器的救世主
- Hadoop3单机部署,实现最简伪集群
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题