- ArrayList(*)
- LinkedList
- Vector(*)
- HashSet(*)
- LinkedHashSet
- TreeSet
- HashMap(*)
- Hashtable
- LinkedeHAshMap
- TreeMap
- Properties
Collection接口实现类的特点2022-4-10 学习笔记
public interface Collection<E> extends Iterable<E>
Collection接口遍历元素方式1一使用Iterator(迭代器)
collection实现子类可以存放多个元素,每个元素都可以是Object
有些Collection实现的类,可以存放重复的元素,有些不可以
有些Collection实现的类,有些是有序的(List),有些不是有序的(Set)
Collection接口没有直接的实现子类,是通过他的子类接口Set和List实现
package main.java.com.chapter14.collection; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @Author BaoYuan * @CreateDate 2022/4/10 15:35 * @Version 1.0 */ public class CollectionMethod { public static void main(String[] args) { List list = new ArrayList(); // add:添加单个元素 list.add("数学"); list.add("英语"); // remove:删除指定元素 list.remove("数学"); list.remove(0); // contains:查找元素是否存在 list.contains("数学"); // size:获取元素个数 list.size(); // isEmpty:判断是否为空 list.isEmpty(); // clear:清空 list.clear(); // addAll:添加多个元素 List list2=new ArrayList(); list2.add("政治"); list2.add("数据结构"); list.addAll(list2); // containsAll:查找多个元素是否存在 list.containsAll(list2); // removeAll:删除多个元素 list.removeAll(list2); // ArraysList演示 } }
迭代器的执行原理
- Iterator对象称为迭代器,主要用于便利Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个Iterator()方法,用以返回一个实现了Iterator接口的对象,即可返回一个迭代器
- Iterator的结构
- Iterator仅用于遍历集合,Iterator本身并不存放对象
- 快捷键:itit
Iterator iterator = coll.iterator;//得到一个集合的迭代器
// hasNext();判断是否还有下一个元素
while(iterator.hasNext()){
//next():①指针下移 ②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}//在调用iterator.next()方法之前必须要调用tierator.hasNext()方法进行检测,若不调用,且下一条记录无效,直接调用tierator.next()方法会抛出 NoSuchElementException异常
代码演示
package main.java.com.chapter14.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @Author BaoYuan
* @CreateDate 2022/4/10 16:04
* @Version 1.0
*/
public class CollectionIterator {
public static void main(String[] args) {
Collection list =new ArrayList();
list.add(new Book("三锅","张三"));
list.add(new Book("税负","三"));
list.add(new Book("就覅","里斯"));
Iterator iterator=list.iterator();
while (iterator.hasNext()) {//判断是否还有数据
//返回下一个元素,类型是Object
Object next = iterator.next();
System.out.println("book="+next);
}
// 循环结束,iterator迭代器指向最后一个元素
// 是用 iterator.next() 会抛出 NoSuchElementException异常
// 再次便利使用,iterator = col.iterator() 即可
}
}
class Book{
private String name;
private String author;
public Book(String name, String author) {
this.name = name;
this.author = author;
}
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;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
}
Collection接口遍历元素方式2一增强for循环
课堂练习 增强for循环,可以替代iterator迭代器,特点:增强for循环就是简化了iterator,本质一样,只能用于遍历集合或数组。
快捷键:I
基本语法:
for(String(元素类型) obj(元素名):list(集合名或数组)){}
List接口和常用方法请编写程序CollectionExercise.java
1.创建3个 Dog {name, age}对象,放入到ArrayList 中,赋给List 引用2.用迭代器和增强for循环两种方式来遍历
3.重写Dog的toString方法,输出name和age
List常用方法List接口时Collection接口的子接口
- List集合类中元素有序(添加于取出的顺序一致,且可重复)
- List集合中的每个元素都有其对应的顺序索引,即支持索引
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
- JDK API中List接口实现类有:AbstractList,AbstractSequentialList,ArrayList,AttributeList,CopyWriteArrayList,LinkedList,RoleList,RoleUnresolveList,Stack,Vector
代码演示
package main.java.com.chapter14.list;
import java.util.ArrayList;
import java.util.List;
/**
* @Author BaoYuan
* @CreateDate 2022/4/10 16:36
* @Version 1.0
*/
public class ListMethod {
public static void main(String[] args) {
List list=new ArrayList();
list.add("张三丰");
list.add("张三丰");
list.add("贾宝玉");
//void add(int index,Object ele) 在index位置插入ele
list.add(1,"张三");
//boolean addAll(int index,Collection eles) 从index位置开始将eles所有元素添加进来
List list2 = new ArrayList();
list2.add("hs");
list2.add("aa");
list.addAll(1,list2);
//int indexOf(Object obj) 返回obj在当前集合中首次出现的位置
list.indexOf("张三");
//int lastIndexOf(Object obj) 返回obj在当前集合中末次出现的位置
list.lastIndexOf("张三");
//Object remove(int index) 删除指定index位置的元素,并返回次元素
list.remove(1);
//Object set(int index,Object ele) 设置指定位置元素为ele,相当于替换
list.set(1,"aa");
//List subList(int fromIndex,int toIndex) 返回从fromIndex到toIndex位置的子集合 [)
list.subList(0,1);
}
}
List的三种遍历方式[ArrayList、LinkedList、Vector]
方式一
方式二使用iterator
Iterator iterator = col.iterator; while(iterator.hasNext()){ System.out.println(iterator.next()); }
方式三使用增强for循环
for(String item:list){ System.out.println(item); }
课堂练习使用普通for
for(int i=0;i<list.size();i++){ System.out.println(list.get(i)); }
代码实现
package main.java.com.chapter14.list;
import java.util.ArrayList;
import java.util.List;
/**
* @Author BaoYuan
* @CreateDate 2022/4/10 17:13
* @Version 1.0
*/
public class ListExercise02 {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Book("三国演义","xxx",100));
list.add(new Book("红楼梦","xxx",150));
list.add(new Book("水浒传","xxx",90));
list.add(new Book("水浒传2","xxx",200));
sort(list);
for (Object o :list) {
System.out.println(o);
}
}
public static void sort(List list){
int len=list.size();
for (int i = 0; i <len-1; i++) {
for (int j = 0; j <len-i-1 ; j++) {
Book book1=(Book)list.get(j);
Book book2=(Book)list.get(j+1);
if(book1.getPrice()>book2.getPrice()){
list.set(j,book2);
list.set(j+1,book1);
}
}
}
}
}
class Book{
private String name;
private String author;
private int price;
public Book(String name, String author, int 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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price='" + price + '\'' +
'}';
}
}
ArrayList注意事项
ArrayList底层结构和源码分析(重点、难点)
- permits all elements,including null ArrayList可以加入null,并且多个
- ArrayList 是由数组来实现数据存储的
- ArrayList 基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,在多线程情况下,不建议使用ArrayList
- 没有加 synchronized关键字
添加元素源码分析(扩容机制)
- ArrayList中维护了一个Object类型的数组elementData.[debug 看源码] transient Object[] elementData; -》transient 表示瞬间的、短暂的,表示该属性不会被序列号
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始化elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
- 如果使用的是指定大小的构造器,则初始化elementData容量为指定大小,如需扩容,则直接扩容elementData为1.5倍
public ArrayList() {
// 构造器
// 创建了一个空的 elementData数组={}
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public static Integer valueOf(int i) {
// 装箱
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
public boolean add(E e) {
// 1.先确定是否要扩容
// 2.然后再执行赋值
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 该方法确定minCapacity
// 第一次扩容为10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity){
modCOunt++;//防止多线程问题,记录倍修改的次数
if(minCapacity - elementData.length > 0)// 如果elementData的大小不够,就调用grow扩容
grow(minCapacity);
}
private void grow(int minCapacity) {
// 1.真扩容方法
// 2.使用扩容机制来确定要扩容到多大
// 3.第一次newCapacity = 10
// 4.第二次即以后,按照1.5倍扩容
// 5.扩容使用Arrays。copyOf() 保留原来数据
// overflow-conscious code
int oldCapacity = elementData.length; //保留之前的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);// 1.5倍
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 ArrayList(int initialCapacity) {
// 调用构造器发生变化
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
Vector底层结构与源码剖析
Vector与ArrayList比较
Vector底层也是一个对象数组,protected Object[] elementData
Vector是线程同步的,即线程安全,Vector类的 *** 作方法带有synchronized
public synchronized void addElement(E obj) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = obj; }
在开发中,需要线程同步安全时,考虑使用Vector
底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 如果有参数构造器1.5倍; 如果无参数:1.第一次10,第二次开始扩容1.5 |
Vector | 可变数组 | jdk1.0 | 安全,效率不高 | 如果无参,默认10倍,满后按2倍扩容,如果指定大小,则每次按2倍扩容 |
LinkedList底层结构new Vector() 底层
public Vector() { this(10); } public Vector(int initialCapacity) { this(initialCapacity, 0); }
Vector.add(i)
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1);//去前面的ArrayList类似,确定集合容量是否足够 elementData[elementCount++] = e; return true; } //确认容量是否足够 private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } //扩容 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); }
LinkedList底层 *** 作机制
- LinkedList实现了双向链表和双端队列特点
- 可以添加任意元素,元素可重复,包括null
- 线程不安全,没实现同步
双向链简单实现
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性frist和last,分别指向首节点和尾节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双线链表
- 所以LinkedList的元素添加和删除,不是通过数组完成,相对来说效率较高
package main.java.com.chapter14.list;
/**
* @Author BaoYuan
* @CreateDate 2022/4/10 22:32
* @Version 1.0
*/
public class LInkedList01 {
public static void main(String[] args) {
Node tom = new Node("tome");
Node jack = new Node("jack");
Node Bao = new Node("Bao");
Node first=new Node(null);
Node last=new Node(null);
tom.next=jack;
jack.next=Bao;
Bao.pre=jack;
jack.pre=tom;
first.next=tom;
last.pre=Bao;
Node tem=first.next;
while(tem!=null){
System.out.println(tem);
tem=tem.next;
}
}
}
class Node{
private Object name;
public Node pre;
public Node next;
public Node(Object name) {
this.name = name;
}
@Override
public String toString() {
return "Node{" +
"name='" + name + '\'' +
'}';
}
}
LinkedList的增删改查案例
ArrayList与LinkedList比较执行 LinkedList linkedList=new LinkedList();
public LinkedList() {} //此时,first与last=null
当 linkedList.add(ele)时,执行
public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) { final Node<E> l = last;// 不加final修饰符,l会随着last而修改自己地址 final Node<E> newNode = new Node<>(l, e, null);//Node(Node
prev, E element, Node last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }next)
底层结构 | 增删的效率 | 改查的效率 | |
---|---|---|---|
ArrayList | 可变数组 | 较低、数组扩容 | 较高 |
LinkedList | 双向链表 | 较高、通过链表追加 | 较低 |
Set接口和常用方法
- 改查的 *** 作多,选择ArrayList
- 增删 *** 作多,选择LinkedList
- 一般来说,程序80%~90%都是查询,因此大部分都采取ArrayList
- ArrayList与LinkedList都是线程不安全,最好都在单线程时使用
2022-4-11学习笔记
Set接口的遍历方式Set接口基本介绍
- 无序(添加和取出顺序不一样,但它是固定的),没有索引
- 不允许重复元素,所以最多包含一个null
- JDK API的Set接口实现类主要有:HashSet、TreeSet、LinkedHashSet
- Set接口和List一样,Set接口也是Collection的子接口,因此常用方法和Collection接口一样
HashSet全面说明同Collection的遍历方式一样,因为Set接口是Collection接口的子接口
- 可以使用迭代器
- 增强for
- 不可以使用索引的方式获取
简单代码演示
HashSet实现了Set接口
HashSet实际上是HashMap
public HashSet() { map = new HashMap<>(); }
可以存放null值,但只能有一个
HashSet不保证元素是有序的,取决于hash后,在确定索引的结果
不能有重复元素/对象
package main.java.com.chapter14.set;
import java.util.HashSet;
import java.util.Set;
/**
* @Author BaoYuan
* @CreateDate 2022/4/11 19:45
* @Version 1.0
*/
public class HashSet_ {
public static void main(String[] args) {
Set hashSet = new HashSet();
hashSet.add("jack");//Ok
hashSet.add("jack");//No
hashSet.add(new Dog("tom"));//Ok
hashSet.add(new Dog("tom"));//Ok
//经典面试题
hashSet.add(new String("cat"));//Ok
hashSet.add(new String("cat"));//No
System.out.println("set="+hashSet);
}
}
class Dog{
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
HashSet底层机制说明
模拟代码演示HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)
package main.java.com.chapter14.set;
/**
* @Author BaoYuan
* @CreateDate 2022/4/11 20:03
* @Version 1.0
*/
public class HashSetStructure {
public static void main(String[] args) {
//模拟一个HashSet的(HashMap底层)数组+链表
Node[] table=new Node[16];
Node jack = new Node("jack", null);
//将jack放在table下标为2下
table[2]=jack;
Node lucy = new Node("lucy", null);
//使将lucy挂载在jack下
jack.next=lucy;
Node tom = new Node("tom", null);
table[3]=tom;
System.out.println(table);
}
}
class Node{
private String name;
Node next;
public Node(String name, Node next) {
this.name = name;
this.next = next;
}
}
HashSet扩容机制
源码解读
- HashSet底层使HashMap
- 添加一个元素时,先得到hash值,会转成->索引值
- 找到存储数据表table,看这个索引位置是否已经存放有元素
- 如果没有直接添加
- 如果有,调用equals()比较(不可的理解成比较内容,每个类有自己的方法,可由程序员重写该方法),如果相同,就放弃添加,如果不同,则添加道最后
- 在Java8重,如果一条链表元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
分析HashSet扩容和转红黑树机制执行 **HashSet hashSet=new HashSet(); **调用
public HashSet() { map = new HashMap<>(); }
执行 hashSet.add(“jack”);
//2. public boolean add(E e) { // e:"jack" //PRESENT=>占位功能(private static final Object PRESENT = new Object();) return map.put(e, PRESENT)==null;//map:"{}" e:"jack" } //3. public V put(K key, V value) { // key:"jack" value:PRESENT //hash(key)计算hash值 存放值表中的值 return putVal(hash(key), key, value, false, true); } //4. static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } //5. 真正重要的方法 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { //定义了辅助变量 Node<K,V>[] tab; Node<K,V> p; int n, i; // table 就是 HashMap的一个数组 Node[] // if当前table是null,或者 大小=0;就是一次扩容,大小=16 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 根据key,得到hash 去计算该key应该存放在table表的哪个索引位置,并把这个位置的对象 // 赋给p // 判断p是否等于null,如果等于null,说明该位置还未存放数据 // 就创建一个Node(key="jack",value=PRESENT)在HashMap中value是自己输入的值 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else {//当传入重复数据,直接这个 Node<K,V> e; K k; // 如果当前索引所指向链表的第一个元素与与准备添加的key的hash值一样 //并且满足以下两个条件之一 //(1)准备添加入的key与p指向的节点key是同一对象 //(2)p指向的Node节点的key的equals()和准备加入的key相比较后市相同的 //就不可添加 if (p.hash == hash && // p 直接指向p = tab[i = (n - 1) & hash] ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //再判断p是否市红黑树 //如果是,则调用putTreeVal()进行添加元素 else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //如果table对应的索引的位置还不是红黑树,是个链表,就是用下面for循环进行比 较 //(1)依次和该链表的每个元素比较后,如都不相同,则直接添加该元素添加到链表最 后 // 注意:添加新元素后,马上对链表的长度进行判断,如果添加元素后,链表的长度 达到8个结点,就调用treeifyBin()对当前的链表进行树化(红黑树) // 注意:在树化之前,treeifyBin()方法还需判断MIN_TREEIFY_CAPACITY=64 // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) // resize(); // 如果上面条件成立,先对table扩容, // 只有上面条件不成立时,才进行转成红黑树 //(2)依次对该链表比较后,如发现有相同情况,直接break for (int binCount = 0; ; ++binCount) { //判断时,直接对辅助变量e赋值,让它指向p的下一个结点 if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD(8) - 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;//在HashMap中实现一样key 替换旧的value afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
- HashSet底层时HashMap,第一次添加时,table数组扩容到16,临界值(threshold)时16*加载因子(loadFactor)是0.75=12
- 如果table数组使用到了临界值12,就会扩容到16*2=32,新的临界值就是32*0.72=24,以此类推
- 在Java8中,如果一条链表的元素个数到了TREEIEY_THRESHOLD(默认是8),并且table大小>=MIN_TREEIEY_CAPACITY(默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制
HashSet课堂练习12022-4-12笔记
- 在HashSet中,添加元素 时,不一定需要在table上添加table的size才增加。只要有元素添加至table中,不管是在table格子上还是某一格子的链表上,size都增加
HashSet课堂练习2定义一个Employee类,该类包含:private成员属性name,age
要求:1.创建3个Employee放入HashSet中
2.当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中package main.java.com.chapter14.set; import java.util.HashSet; import java.util.Objects; import java.util.Set; /** * @Author BaoYuan * @CreateDate 2022/4/12 15:46 * @Version 1.0 */ public class HashSetExercise { public static void main(String[] args) { Set hashSet = new HashSet(); hashSet.add(new Employee("jack",20)); hashSet.add(new Employee("jack",20)); hashSet.add(new Employee("jack",22)); hashSet.add(new Employee("tome",22)); System.out.println(hashSet); } } class Employee{ private String name; private int age; public Employee(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 == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return age == employee.age && Objects.equals(name, employee.name); } @Override public int hashCode() { return Objects.hash(name, age); } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
定义一个Employee类,该英包含:private员属性name,sth,adau)a求:ya一型),其中 birthday 为 MyDate类型(属性包括: year, month, day),
要求:
1.创建3个Employee放入HashSet中
2当name和birthday的值相同时,认为是相同员工,不能添加到HashSet集合中package main.java.com.chapter14.set; import java.util.HashSet; import java.util.Objects; /** * @Author BaoYuan * @CreateDate 2022/4/12 16:03 * @Version 1.0 */ public class HashSetExercise02 { public static void main(String[] args) { HashSet hashSet = new HashSet(); hashSet.add(new Employee("jack",22,new MyDate(2000,1,1)));//ok hashSet.add(new Employee("jack",22,new MyDate(2000,1,1)));//no hashSet.add(new Employee("jack",22,new MyDate(2000,2,1)));//ok System.out.println(hashSet); } } class MyDate{ private int year; private int month; private int day; public MyDate(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyDate myDate = (MyDate) o; return year == myDate.year && month == myDate.month && day == myDate.day; } @Override public int hashCode() { return Objects.hash(year, month, day); } @Override public String toString() { return "MyDate{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; } } class Employee{ private String name; private int sal; private MyDate birthday; public Employee(String name, int sal, MyDate birthday) { this.name = name; this.sal = sal; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSal() { return sal; } public void setSal(int sal) { this.sal = sal; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return Objects.equals(name, employee.name) && Objects.equals(birthday, employee.birthday); } @Override public int hashCode() { return Objects.hash(name, birthday); } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", sal=" + sal + ", birthday=" + birthday + '}'; } }
LinkedHashSet的全面说明LinkedHashSet课后练习题
- LinkedHashSet是HashSet的子类
- LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
- LInkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是插入顺序保存的
- LinkedHashSet不允许添加重复元素
*
TreeSet介绍Car类(属性:name,price),如果name和price-样,则认为是相同元素,就不能添加。
package main.java.com.chapter14.set; import java.util.LinkedHashSet; import java.util.Objects; /** * @Author BaoYuan * @CreateDate 2022/4/12 17:19 * @Version 1.0 */ public class LinkedHsahSetExercise { public static void main(String[] args) { LinkedHashSet linkedHashSet = new LinkedHashSet(); linkedHashSet.add(new Car("三轮车",100)); linkedHashSet.add(new Car("三轮车",100)); linkedHashSet.add(new Car("三轮车",10)); System.out.println(linkedHashSet); } } class Car{ private String name; private int price; public Car(String name, int price) { this.name = name; this.price = price; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Car car = (Car) o; return price == car.price && Objects.equals(name, car.name); } @Override public int hashCode() { return Objects.hash(name, price); } @Override public String toString() { return "Car{" + "name='" + name + '\'' + ", price=" + price + '}'; } }
Map接口和常用方法 Map接口特点
当我们用无参构造器创建时,仍时无序的
TreeSet treeSet = new TreeSet(new Comparator() { @Override public int compare(Object o1, Object o2) { //调用String的比较方法实现从大到小排序 return ((String)o2).compareTo((String)o1); } });
其内部排序的方式有compar()方法决定
Map常用方法
- Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
- Map中key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不可重复,原因和HashSet一样
- Map中value可以重复
- Map中key可以为null,value也可以为null,注意:key为null,只能有一个,value为null,可以多个(key还是以hash值存储在table中,而value不是以hash值存放至表中,在不同的table位置上可以存在同个value)
- 常用String类作为Map的key
- key和value之间在单向一对一关系,即通过指定的key总能找到对应的value
- k-v最后是HashMap$Node node = newBode(hash,key,value,null)
- k-v 为方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素类型是Entry,里面一个Entry对象就有k,v EntrySet
>
*
2022-4-13学习笔记
Map接口遍历方式
- put(k,v) 添加
- remove(key) 根据键值删除映射关系
- get(key) 根据键值获取值
- size() 获取元素个数
- isEmpty() 判断是否个数为0
- clear() 清除
- containsKey(key) 查找键值是否存在
代码演示遍历方式
- containsKey() 查找键值
- keySet() 获取所有的键
- entrySet() 获取所有的关系
- values 获取所有的值
package main.java.com.chapter14.map;
import java.util.*;
/**
* @Author BaoYuan
* @CreateDate 2022/4/13 15:02
* @Version 1.0
*/
@SuppressWarnings({"all"})
public class MapFor {
public static void main(String[] args) {
Map map=new HashMap();
map.put("a","b");
map.put("c","d");
map.put("e","f");
map.put("g","h");
map.put("j","k");
// 第一种方式
System.out.println("----使用keySet 加 增强for循环遍历----");
Set keys = map.keySet();
for (Object key : keys) {
System.out.println(key + "-" + map.get(key));
}
System.out.println("----使用keySet 加 迭代器遍历----");
Iterator iterator1= keys.iterator();
while (iterator1.hasNext()) {
Object key = iterator1.next();
System.out.println(key + "-" + map.get(key));
}
//第二种方式 只是获取值
System.out.println("----使用values() Collection 获取到值----");
Collection values=map.values();
for (Object value :values) {
System.out.println(value);
}
//第三种方式
System.out.println("----使用entrySet----");
Set entrySet = map.entrySet();
for (Object entry :entrySet) {
Map.Entry entry1=(Map.Entry) entry;
System.out.println(entry1.getKey() + "-" + entry1.getValue());
}
}
}
Map课堂练习
HashMap小结使用HashMap添加3个员工对象,要求键:员工id
值:员工对象
并遍历显示工资>18000的员工(遍历方式最少两种)员工类:姓名、工资、员工idpackage main.java.com.chapter14.map; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @Author BaoYuan * @CreateDate 2022/4/13 15:23 * @Version 1.0 */ public class MapExercise { public static void main(String[] args) { Map map=new HashMap(); map.put(1,new Employee(1,"jack",1500.00)); map.put(2,new Employee(2,"tom",18000.00)); map.put(3,new Employee(3,"lucy",110800.00)); System.out.println("----第一种遍历方式----"); Set keySet = map.keySet(); for (Object key :keySet) { Employee employee=(Employee)map.get(key); if(employee.getSal()>=18000){ System.out.println(employee); } } System.out.println("----第二种遍历方式----"); Set entrySet=map.entrySet(); for (Object entry:entrySet) { Map.Entry entry1=(Map.Entry)entry; Employee employee=(Employee)entry1.getValue(); if(employee.getSal()>=18000){ System.out.println(employee); } } } } class Employee{ private int id; private String name; private double sal; public Employee(int id, String name, double sal) { this.id = id; this.name = name; this.sal = sal; } 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; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", sal=" + sal + '}'; } }
HashMap底层机制及源码剖析 示意图 扩容机制(和HashSet相同)
- Map接口常用实现类:HashMap,HashTable,Properties
- HashMap是Map接口使用最频繁的实现类
- HashMap是以key-value对的方式来存储数据(HashMap$Node)
- key不可重复添加,但是值可以重复添加,允许使用null键和null值
- 如果添加相同的key,则会覆盖原来的key-value,等同于修改(key不会替换,value会替换)
- 与HashSet一样,不保证映射的顺序,因为底层以hash表的方式来存储
- HashMap没有实现同步,因此线程不安全。方法没有做同步互斥 *** 作,没有synchronized
HashTable介绍
- HashMap底层维护了Node类型的数组table,默认为null
- 当创建对象时,将加载因子(loadfactor)初始化为0.75
- 当添加key-value时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引有元素,继续判断该元素的key是否和准备加入的key相等,如果相等,则替换value;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果在添加时,容量不够,则需要扩容
- 第一次添加,需扩容至16,临界值为12(16*0.75)
- 以后再扩容,则需要扩容table容量为原来的2倍,临界值变为原来的2倍,即24,依次类推
- 在Java8中,如果一条链表的元素个数超过TREEIEY_THRESHOLD(默认8),并且table大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
HashMap与HashTable对比
- 存放的元素是键值对:即k-v
- HashTable的键和值都不能为null,否则会抛出NullPointerException
- HashTable使用的方法基本和HashMap一致
- HashTable是线程安全的(synchronized),HashMap是线程不安全的
- 底层有数组HashTable$Entry[] 初始化大小为11
- 临界值 threshold=8(11*0.75)
- 扩容按照自己的扩容机制执行((oldCapacity<<1)+1) 在原先基础✖2+1
- 简单看下底层结构
版本 | 线程安全(同步) | 效率 | 允许null键null值 | |
---|---|---|---|---|
HashMap | 1.2 | 不安全 | 高 | 可以 |
HashTable | 1.0 | 安全 | 较低 | 不可以 |
总结-开发中如何选择集合实现类(记住)
- Properties类继承HashTable类并实现了Map接口,也是一种键值对的形式来保存数据
- 他的使用特点和HashTable类似
- Properties还可以用于从 xxx/properties文件种,加载数据到Properties类对象并进行读取和修改
- 说明:工作后 xxx.properties文件通常作为配置文件,这个知识点在IO流举例
Collections工具类在开发中,选择什么集合实现类,取决于业务 *** 作特点,然后根据集合实现类特性进行选择,分析如下
- 先判断存储的数据类型(一组对象[单列]或一组键值对[双])
- 一组对象:Colllection接口
- 允许重复:List
- 增删多:LinkedLIst(底层维护了一个双向链表)
- 改查多:ArrayList(底层维护了一个Object类型的可变数组)
- 不允许重复:Set
- 无序:HashSet(底层是HashMap,维护了一个哈希表,即数组+链表+红黑树
- 排序:TreeSet
- 插入和取出顺序一致:LinkedHashSet【底层LinkedHahsMap->HashMap】(底层维护了 数组+双向链表)
- 一组键值对:Map
- 键无序:HashMap(底层是哈希表+jdk7(数组+链表) jdk8(数组+链表+红黑树)
- 键排序:TreeMap
- 键插入和去除顺序一样:LinkedHashMap
- 读取文件:Properties
章节作业 作业1
- Collections是一个 *** 作Set、List和Map等集合的工具类
- Collections提供了一系列静态的方法对集合元素进行排序、查询和修改等 *** 作
- 排序 *** 作
- reverse(List):反转List种元素的顺序
- shuffle(List):对List集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定的List集合元素升序排序
- sort(List,Comparator):根据指定的Comparator产生的顺序对List集合进行排序
- swap(List,int,int):对指定List集合的i处元素和j处元素进行交换
- 查找、替换
- Object max(Collection):根据元素的自然排序,返回给定集合最大的元素
- Object max(Collection,Comparator):根据Comparator指定的顺序、返回给定集合种最大元素
- Object min(Collection):
- Object min(Collection,Comparator):
- int frequency(Collection,Object):返回指定集合种指定元素出现的次数
- void copy(List dest,List src):将src的内容复制到dest中
- 在拷贝中,dest的长度需要大于或者等于src的长度,否则报出异常
- boolean replaceAll(List list,Object odlVal,Object newVal):使用新值替换List对象的所有旧值
按要求实现:
(1)封装一个新闻类,包含标题和内容属性,提供get、set方法,重写toString方法,打印对象时只打印标题;
(2)只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:新闻一:新冠确诊病例超千万,数百万印度教信徒赴恒河“圣浴”引民众担忧新闻二:男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生
(3)将新闻对象添加到ArrayList集合中,并且进行倒序遍历;
(4)在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前15个,然后在后边加“.…."
(5)在控制台打印遍历出经过处理的新闻标题;
代码
package main.java.com.chapter14.homeWork;
import java.util.ArrayList;
/**
* @Author BaoYuan
* @CreateDate 2022/4/14 16:53
* @Version 1.0
*/
public class homeWork01 {
public static void main(String[] args) {
news news_1 = new news("新冠确诊病例超千万,数百万印度教信徒赴恒河'\"'圣浴'\"'引民众担忧");
news news_2 = new news("突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生");
ArrayList arrayList = new ArrayList();
arrayList.add(news_1);
arrayList.add(news_2);
for(int i=arrayList.size()-1;i>=0;i--){
news tem=(news)arrayList.get(i);
String title=processTitle(tem.getTitle());
tem.setTitle(title);
System.out.println(tem);
}
}
public static String processTitle(String title){
if(title==null){
return "";
}
if(title.length()>15){
return title.substring(0,15)+"....";
}else{
return title;
}
}
}
class news{
private String title;
private String content;
public news(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "news{" +
"title='" + title + '\'' +'}';
}
}
作业2
使用ArrayList完成对对象Car {name, price}的各种 *** 作
1.add:添加单个元素
2.remove:删除指定元素Car car = new Car(“宝马”,400000);
3.contains:查找元素是否存在Car car2 = new Car(“宾利”,5000000);
4.size:获取元素个数
5.isEmpty:判断是否为空
6.clear:清空
7.addAll:添加多个元素
8.containsAll:查找多个元素是否都存在9.removeAll:删除多个元素 使用增强for和迭代器来遍历所有的car,需要重写Car 的toString方法
代码
package main.java.com.chapter14.homeWork;
import java.util.ArrayList;
/**
* @Author BaoYuan
* @CreateDate 2022/4/14 17:09
* @Version 1.0
*/
public class homeWork02 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(new Car("宝马",500000.0));
arrayList.add(new Car("奥迪",502000.0));
arrayList.add(new Car("凯迪拉克",400000.0));
Car baoma = new Car("宝马",500000.0);
Car binli = new Car("宾利",500000.0);
arrayList.remove(baoma);
Boolean binli_contains=arrayList.contains(binli);
}
}
class Car{
private String name;
private double price;
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
作业3
按要求完成下列任务
1)使用HashMap类实例化一个Map类型的对象m,键(String)和值(int)分别用于存储员工的姓名和工资,存入数据如下:jack—650元; tom-1200元;smith-
2900元;
2)将jack的工资更改为2600元3)为所有员工工资加薪100元;
4)遍历集合中所有的员工
5)遍历集合中所有的工资
代码
package main.java.com.chapter14.homeWork;
import java.util.*;
/**
* @Author BaoYuan
* @CreateDate 2022/4/14 17:15
* @Version 1.0
*/
public class homeWork {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("jack",650);
hashMap.put("tom",12000);
hashMap.put("smith",2900);
System.out.println(hashMap);
hashMap.replace("jack",2600);
//加薪
Set keySet=hashMap.keySet();
for (Object key :keySet) {
hashMap.replace(key,(int)hashMap.get(key)+100);
}
//遍历
Set entrySet = hashMap.entrySet();
for (Object entry :entrySet) {
Map.Entry entry1=(Map.Entry)entry;
System.out.print(entry1.getKey()+"-"+entry1.getValue());
}
System.out.println();
System.out.println(hashMap);
//遍历工资
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
System.out.println("工资:"+entry.getValue());
}
Collection values = hashMap.values();
for (Object value :values) {
System.out.println("工资"+value);
}
}
}
class employee{
private String name;
private int sal;
public employee(String name, int sal) {
this.name = name;
this.sal = sal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSal() {
return sal;
}
public void setSal(int sal) {
this.sal = sal;
}
@Override
public String toString() {
return "employee{" +
"name='" + name + '\'' +
", sal=" + sal +
'}';
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)