集合用法
一、集合概述
-
集合用于存储数量的不确定的对象
-
集合和数组是不一样的(a、数组既可以存储基本类型的值,也可以是对象,而集合里只能保存对象。
数组长度不可更改,集合长度可以动态 )
-
集合比数组存储数据更加灵活
-
集合的图形表
-
List的实现类
Vector
ArrayList
LinkedList
Stack -
Set的实现类
HashSet
TreeSet
LinkedHashSet -
Map的实现类
HashMap
TreeMap
LinkedHashMap
HashTable -
通过Map接口,可以产生Collection接口对象
-
Iterator
迭代器
通过Collection,获取迭代器对象 -
ListIterator()
1、集合分类
集合可以分为(Set、List、Map)其实也是集合接口
- List集合(能存储可重复的对象,且也是有序的)这个指的是,你怎么存发数值,他就是怎么输出
一开始给的值: 1,2,3,4,6,5
遍历出来的指: 1,2,3,4,6,5
package demo.cn.com;
import java.util.ArrayList;
import java.util.Iterator;
class Dog{
private String name;
public Dog(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog [name=" + name + "]";
}
}
public class DemoTest {
public static void main(String[] args) {
ArrayList<Dog> arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("bb"));
arrayList.add(new Dog("dd"));
arrayList.add(new Dog("aa"));
arrayList.add(new Dog("ee"));
arrayList.add(new Dog("ff"));
Iterator<Dog> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Dog dog = (Dog) iterator.next();
System.out.println(dog);
}
//遍历输出可以是foreach 或者是Iterator
// for (Dog dog : arrayList) {
// System.out.println(dog);
// }
}
}
- Set集合(不能存储可重复的对象,无序)这个指的是,你怎么存发数值,它随机打印输出
一开始给的值: A,B,C,D,E,F
遍历出来的指: D,B,A,C,F,E
HashSet<Integer> hashSet1 = new HashSet<Integer>();
hashSet1.add(11);
hashSet1.add(22);
hashSet1.add(11);
hashSet1.add(22);
hashSet1.add(33);
hashSet1.add(99);
hashSet1.add(44);
hashSet1.add(55);
hashSet1.add(66);
hashSet1.add(77);
hashSet1.add(88);
for (Integer integer : hashSet1) {
System.out.println(integer);
}
- Queue集合(队列结合)FIFO 双 端队列
- Map集合 (它能够存储俩个对象)key-value 映射关系
2、contains的用法
- 底层源码是调用了equals,且 用来比较两个对象的内容是否相等
二、Collection和Iterator接口
1、Collection的简介
-
Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法即可用于操作Set集合,也可用于操作List和Queue集合
-
boolean add(Object o):添加对象到集合中
Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。
Object[] toArray():该方法把集合转换成一个数组,所有的集合元素变成对应的数组元素。
- 注意!!!! 当collcetion没有重写equalis方法时,比较的是内存地址,当重写的时候,比较的是内容
2、collection的主要方法
package day23.demo.cn.homework;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class MainTest3 {
public static void main(String[] args) {
Collection collection = new HashSet();
collection.add("a");
collection.add("b");
collection.add("c");
collection.add("d");
collection.add("e");
collection.add("f");
collection.add("g");
collection.add("h");
collection.add("i");
collection.add("j");
Iterator iterator = collection.iterator();
iterator.forEachRemaining(s-> System.out.println(s));
}
}
3、Iterator的简介
- Iterator接口也是Java集合框架的成员,但它与Collection系列、Map系列的集合不一样,它主要用于遍历集合中的元素。
- boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。
- Object next():返回集合里的下一个元素
- void remove():删除集合里上一次next方法返回的元素
三、Set集合用法
1、概述
- Set集合复用了Collection的方法,同时也扩展自身的特点,它是基于 HashMap 实现的,底层采用 HashMap 来存储元素
- 不能添加相同的对象数据
- 数据是无序的
2、HashSet的用法
-
以哈希码方式存储对象
-
HashSet是线程不安全
-
不能存储相同的对象
- 判断对象是否相同的标准:根据 hasCode方法 的返回值及 equals方法 来决定对象是否相同的标准
- 如果hasCode方法的返回值相同,还有equals方法的返回值为
true,则认为对象相同
- 如果hasCode方法的返回值相同,还有equals方法的返回值为
- 判断对象是否相同的标准:根据 hasCode方法 的返回值及 equals方法 来决定对象是否相同的标准
package demo.cn.com;
import java.util.HashSet;
class Student1{
private int count;
public Student1() {
}
public Student1(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Student [count=" + count + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + count;
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Student1) {
Student1 student = (Student1) obj;
if (this.getCount() == student.getCount()) {
return true;
}
}
return false;
}
}
public class HashSetTest {
public static void main(String[] args) {
HashSet<Student1> set=new HashSet<>();
set.add(new Student1(10));
set.add(new Student1(2));
set.add(new Student1(50));
set.add(new Student1(40));
set.add(new Student1(90));
set.add(new Student1(90));
Student1 r100=new Student1(100);
set.add(r100);
set.add(r100);
for (Student1 student : set) {
System.out.println(student);
}
}
}
- 这个是当没有重写Hashcode跟equals方法的,不是说是HashSet不是无序跟无重复的吗?为什么会出现重复
因为 set.add(new Student1(90)); 新建了一个对象,而这个对象的hash值是不同的,就会出现如下的情况
Student[count=10] Student[count=40] Student[count=90] Student[count=50]
Student[count=100] Student[count=2] Student[count=90]
-
重写了hashcode 跟 equals(进行位置跟内容判断)就不会出现数值相同的情况了
Student [count=2] Student [count=50] Student [count=100] Student [count=40] Student [count=10] Student [count=90]
3、HashSet的存储方式
- HashSet存储对象以哈希码存储
- 每个hascode的值,分配一个槽(slot),同一个槽的对象,以链表方式存储
- 存储方式如下:
4 、TreeSet的用法
- TreeSet可以确保集合元素处于
排序
状态 - TreeSet也是属于Set集合类别一种
- 不能存储相同的对象
- 保证集合对象以
排序
状态显示- 自然排序实现(TreeSet的默认排序)
- 定制排序实现
- TreeSet 默认调用的是TreeMap方法:底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序
a、自然排序
- 自然排序是TreeSet排序的默认规则
- 此类必须要实现Comparable接口
- 重写compareTo方法
- 如果该方法返回0,则表明这两个对象相等;如果该方法返回
一个正整数,则表明obj1大于obj2;如果该方法返回一个
负整数,则表明obj1小于obj2
package demo.cn.com;
//这个是使用自然排序实现
import java.util.TreeSet;
class Student1{
private int count;
public Student1() {
}
public Student1(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Student [count=" + count + "]";
}
}
public class HashSetTest {
public static void main(String[] args) {
TreeSet<Student1> set=new TreeSet<>();
set.add(new Student1(10));
set.add(new Student1(2));
set.add(new Student1(50));
set.add(new Student1(40));
set.add(new Student1(90));
set.add(new Student1(90));
Student1 r100=new Student1(100);
set.add(r100);
set.add(r100);
for (Student1 student : set) {
System.out.println(student);
}
}
}
点击运行的代码时候报错,这个里面的报错指的TreeSet的接口是SortsTree,也是实现排序,也就是说这个接口需要实现Comparable来重新compareTo就行数值比较,而实现排序
修改的代码:
b、定制排序
-
是通过实现Comparator接口重现compare接口,判断里面的值是返回-1、0、1
-
此类不需要实现Comparable接口
- 如果需要实现定制排序,则需要在创建TreeSet集合对象时,提供一个
Comparator对象与该TreeSet集合关联,由该Comparator对象负责
集合元素的排序逻辑。
- 如果需要实现定制排序,则需要在创建TreeSet集合对象时,提供一个
-
package demo.cn.com; import java.util.Comparator; import java.util.TreeSet; class Phone{ private String phoneName; private int score; private int weight; public Phone() { } public Phone(String phoneName, int score, int weight) { super(); this.phoneName = phoneName; this.score = score; this.weight = weight; } @Override public String toString() { return "Phone [phoneName=" + phoneName + ", score=" + score + ", weight=" + weight + "]"; } public String getPhoneName() { return phoneName; } public void setPhoneName(String phoneName) { this.phoneName = phoneName; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } } public class PhoneTest { public static void main(String[] args) { TreeSet<Phone> set = new TreeSet<Phone>(new Comparator<Phone>() { @Override public int compare(Phone o1, Phone o2) { if (o1.getScore()>o2.getScore()) { return 1; } else if (o1.getScore()<o2.getScore()){ return -1; }else { return 0; } } }); for (int i = 0; i < 10; i++) { int score = (int) (Math.random()*1000); int weight = (int) (Math.random()*10); Phone phone = new Phone(); phone.setPhoneName("型号"+i); phone.setScore(score); phone.setWeight(weight); set.add(phone); } for (Phone phone : set) { System.out.println(phone); } } }
c、实现TreeSet低层算法
- 实现红黑树数据结构实现排序功能
1.节点是红色或黑色。
2.根节点是黑色。
3.每个叶子节点都是黑色的空节点(NIL节点)。
4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
5、Comparable的描述
-
Java的一些常用类已经实现了Comparable接口,并提供了比较大小的标准。下面是实现了Comparable接口的常用类:
-
BigDecimal、BigInteger以及所有的数值型对应的包装类;按它们对应的数值大小进行比较。
-
Character:按字符的UNICODE值进行比较。
-
Boolean:true对应的包装类实例大于false对应的包装类实例。
-
String:按字符串中字符的UNICODE值进行比较。
-
Date、Time:后面的时间、日期比前面的时间、日期大
四、LinkedHashSet集合用法
1、简介
- LinkedHashSet 是 HashSet 的子类
- LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
- LinkedHashSet 不允许集合元素重复。
2、分析存储数据原理
- 链表形式存在
- 以哈希码存储数据
- 数据与数据之间使用链表维护,因此数据是有序输出
package demo.cn.com;
import java.util.LinkedHashSet;
class R{
private int count;
public R() {
}
public R(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "R [count=" + count + "]";
}
}
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet<R> set2=new LinkedHashSet<>();
set2.add(new R(100));
set2.add(new R(50));
set2.add(new R(80));
set2.add(new R(90));
set2.add(new R(20));
set2.add(new R(10));
//有序输出
set2.forEach(System.out::println);
}
}
五、Set集合的性能分析
- 对比HashSet与TreeSet
- 从性能角色:HashSet>TreeSet
- 随机读取能力
- 添加数据能力
- 遍历数据能力
- 从性能角色:HashSet>TreeSet
- 对比HashSet与LinkedHashSet
- 针对读取、写入HashSet要比LinkedHashSet性能要好
- 针对整体遍历,LinkedHashSet要比HashSet性能要好
六、List集合用法
1、简介
- List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引,例如第一个添加的元素索引为0,第二个添加的元素索引为1**(ArrayList是线程不安全)**
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class MainTest {
public static void main(String[] args) {
//创建ArrayList对象
ArrayList<String> books=new ArrayList<>();
books.add("thinking in java");
books.add("thinking in c++");
books.add("thinking in python");
books.add("thinking in c");
System.out.println("写法一:如何遍历list集合");
//如何遍历list集合
books.forEach(System.out::println);
System.out.println("写法二:如何遍历list集合");
for(String v:books){
System.out.println(v);
}
System.out.println("写法三:如何遍历list集合");
Iterator<String> iter=books.iterator();
while(iter.hasNext()){
String value=iter.next();
System.out.println(value);
}
System.out.println("写法四:如何遍历listIterator集合");
ListIterator<String> iterList=books.listIterator();
while(iterList.hasNext()){
String value=iterList.next();
System.out.println(value);
}
System.out.println("listIterator支持反向遍历输出");
while(iterList.hasPrevious()){
String value=iterList.previous();
System.out.println(value);
}
System.out.println("写法五:通过下标值遍历list集合");
for(int index=0;index<books.size();index++){
//根据index,获取对象数据
String book=books.get(index);
System.out.println(book);
}
System.out.println("写法六:将ArrayList转换成数组");
Object []objectArrays=books.toArray();
for(Object o:objectArrays){
if(o instanceof String)
System.out.println((String)o);
}
System.out.println("删除index=2");
books.remove(2);
books.forEach(System.out::println);
}
}
2、List集合的特有迭代器ListIterator
-
与Set只提供了一个iterator()方法不同,List还额外提供了一个listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,在List方法的基础上,还增加了如下方法:
-
boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素
-
Object previous():返回该迭代器的上一个元素
-
void add():在指定位置插入一个元素
3、ArrayList 源码分析
-
针对jdk1.8版本ArrayList
-
底层是使用对象类型的
数组
存储 -
当第一次add方法调用,才实现数组长度的初始化,默认长度10
-
当调用add方法,首先判断 size+1与数组当前的长度的大小,来决定是否扩容
if( size+1>数组.length()) { //数组扩容处理 grow(); }
-
如何扩容数组
-
扩容数组后的数组长度=原先数组的长度*1.5倍
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
-
-
-
如何实现删除
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
- 根据下标,添加数据
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
package 第三题;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Objects;
import java.util.function.Consumer;
public class MyList<E> {
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
private int size;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
public MyList()
{
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public boolean add(E e) {
//计算数组的长度容量
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//主要计算数组的长度
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//当第一次调用add,数组未初始化,默认长度为DEFAULT_CAPACITY常量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果此数组已经存在数据,则直接返回minCapacity值
//minCapacity值=size+1
//size:就是数组的下标
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
// overflow-conscious code
//判断是否数组扩容
//size+1>elementData.length,则进行扩容处理
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
//获取扩容之前的数组长度
int oldCapacity = elementData.length;
//扩容之后的数组长度=1.5*扩容之前的数组长度;
int newCapacity = oldCapacity + (oldCapacity >> 1);
//minCapacity>newCapacity,则newCapacity=minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
//针对数组进行扩容,扩容长度为newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}
E elementData(int index) {
return (E) elementData[index];
}
public E remove(int index) {
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; i < size; i++) {
action.accept(elementData[i]);
}
}
}
4、List的性能比较
七、Map集合
1、Map集合特点(HashMap、TreeMap、LinkedHashMa)
-
存储key,value的具有映射有关系的集合对象
-
key、value都是对象类型,其实就是引用
-
key其实就是Set集合类型
- key不能重复存储相同的对象
-
value就是Collection类型
- 允许可重复的对象
-
key与value之间存在映射关系
- 一个key,只能映射一个value对象
- 但同一个value对象,可以被多个key映射
2、HashMap的具体用法
- HashMap线程不安全
- HashMap可以使用null作为key和value
- HashMap与HashSet一样,判断key的存储对象是否相同的标准:根据hasCode方法的返回值和equals方法的返回值决定此对象是否相同
package demo.cn.com;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
class Shop{
private int shopId;
private String shopName;
public Shop() {
}
public Shop(int shopId, String shopName) {
this.shopId = shopId;
this.shopName = shopName;
}
public int getShopId() {
return shopId;
}
public void setShopId(int shopId) {
this.shopId = shopId;
}
public String getShopName() {
return shopName;
}
public void setShopName(String shopName) {
this.shopName = shopName;
}
@Override
public String toString() {
return "Shop [shopId=" + shopId + ", shopName=" + shopName + "]";
}
@Override
public int hashCode() {
return getShopId();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Shop) {
Shop oteShop = (Shop) obj;
if (oteShop.getShopId() == this.getShopId()) {
return true;
}
}
return false;
}
}
class Person{
private String personName;
private int personAge;
public Person() {
}
public Person(String personName, int personAge) {
this.personName = personName;
this.personAge = personAge;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public int getPersonAge() {
return personAge;
}
public void setPersonAge(int personAge) {
this.personAge = personAge;
}
@Override
public String toString() {
return "Person [personName=" + personName + ", personAge=" + personAge + "]";
}
}
public class HashMapTest {
public static void main(String[] args) {
HashMap<Shop, Person> map = new HashMap<>();
map.put(new Shop(1, "aa"), new Person("张一", 18));
map.put(new Shop(2, "bb"), new Person("张二", 19));
map.put(new Shop(3, "cc"), new Person("张三", 20));
map.put(new Shop(4, "dd"), new Person("张四", 21));
map.put(new Shop(5, "ee"), new Person("张五", 22));
map.remove(new Shop(3, "cc"));
// for (Map.Entry<Shop,Person> entry : map.entrySet()) { //这个是普通的排序方式
// Shop KeyShop = entry.getKey();
// Person ValuePerson = entry.getValue();
// System.out.println(KeyShop);
// System.out.println(ValuePerson);
//
// }
map.forEach((k,v)->System.out.println("key"+v+"value"+v));
}
}
3、TreeMap的具体使用
-
TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。
-
TreeMap两种排序方式(跟TreeMap使用是一样的)
-
自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则将会抛出ClassCastException异常。
-
定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。采用定制排序时不要求Map的key实现Comparable接口中。
package demo.cn.com;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapTest {
public static void main(String[] args) {
//其实跟TreeSet是类似的也是有自然排序跟定制排序
//自然排序
TreeMap<Integer, String> treeMap= new TreeMap<Integer, String>();
treeMap.put(1, "a");
treeMap.put(12, "b");
treeMap.put(3, "c");
treeMap.put(4, "d");
treeMap.put(2, "e");
System.out.println("普通输出");
for (Map.Entry<Integer, String> sEntry: treeMap.entrySet()) {
System.out.println(sEntry);
}
System.out.println("Lambda输出");
treeMap.forEach((k,v)->{
System.out.println(k+"\t"+v);
});
//用匿名内部类来实现数Comparator,不然会爆错,可以参考上面的笔记
TreeMap<Integer, String> treeMap1= new TreeMap<Integer, String>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o1>o2?-1:o1<o2?1:0;
}
});
treeMap1.put(1, "a");
treeMap1.put(12, "b");
treeMap1.put(3, "c");
treeMap1.put(4, "d");
treeMap1.put(2, "e");
System.out.println("定制输出+++普通输出");
for (Map.Entry<Integer, String> sEntry: treeMap1.entrySet()) {
System.out.println(sEntry);
}
System.out.println("定制输出+++Lambda输出");
treeMap1.forEach((k,v)->{
System.out.println(k+"\t"+v);
});
}
}
4、LinkedHashMap的具体使用
- HashSet有一个子类是LinkedHashSet,HashMap也有一个LinkedHashMap子类; LinkedHashMap也使用双向链表来维护key-value对的次序(其实只需要考虑Key的次序),该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。(有序):有序指的是你怎么存入怎么输出是一样的
package demo.cn.com;
import java.util.HashSet;
import java.util.LinkedHashSet;
class R{
private int count;
public R() {
}
public R(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "R [count=" + count + "]";
}
}
class Person1{
private int id;
private String name;
public Person1(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person1 [id=" + id + ", name=" + name + "]";
}
}
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet<R> set2=new LinkedHashSet<>();
set2.add(new R(100));
set2.add(new R(50));
set2.add(new R(80));
set2.add(new R(90));
set2.add(new R(20));
set2.add(new R(10));
//有序输出
set2.forEach(System.out::println);
}
}
5、HashMap的源码分析
- https://www.cnblogs.com/9dragon/archive/2019/12/09/12005142.html介绍HashMap的源码信息
a、HashMap的无参构造
public HashMap() {
//负载因子=0.75
//当数据达到3/4,则进行扩容处理
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
b、put方法中hash值
-
首先是扰动算法,主要是获取一个均衡的hash指
static final int hash(Object key) { int h; //取hashcode方法数据值的高16位与低16位异或操作:就会生成一个均衡的hash值 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
c、put方法
- 参数一:hash:通过上述方法生成(均衡的hash值)
- 参数二:key:添加的key值
- 参数三:value:添加的value值
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
//定义一个数组,数组类型:Node(链表的对象)
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果数组为空,则进行扩容
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//通过hash值获取一个数组单元(桶)
if ((p = tab[i = (n - 1) & hash]) == null)
//如何此数组单元为空对象,则直接添加一个节点对象进来
tab[i] = newNode(hash, key, value, null);
else {
//数组单元已经存在节点对象
Node<K,V> e; K k;
//就比较数组单元已经存在的节点对象与将要添加的key的equals方法和hash值,如果返回true及hash值相同,则认为两者的对象相同
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//将数组单元已经存在的节点对象赋值给e
e = p;
else if (p instanceof TreeNode)
//数组单元已经存在的节点对象如果是树节点,则将节点添加到红黑树
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//遍历已经存在的链表节点,将节点添加到链接
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//如果此链接节点的个数>=7,则构建成一颗红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}