P499~P553
集合
引出问题
之前保存数据使用的是数组,那么数组有不足的地方,我们分析一下
- 长度开始时必须指定,而且一旦指定,不能更改,int[] n =new int[3];
- 保存的必须为同一类型的元素
- 使用数组进行增加元素
Person[] pers = new Person[1];
per[0]=new Person();
集合
- 可以动态保存任意多个对象,使用比较方便
- 提供了一系列方便的操作对象的方法:add、remove、set、get
集合的分类
集合主要分两组(单列集合,双列集合)
单列集合List、Set
Collection 接口有两个重要子接口List、Set,他们的实现子类都是单列集合
- Collection
- Set
- TreeSet
- HashSet
- SortedSet
- List
- Vector
- ArrayList
- LinkedList
- Set
双列集合Map
Map接口的实现子类是双列集合,存放的是key-value
- Map
- Hashtable
- Properties
- HashMap
- LinkedHashMap
- TreeMap
- Hashtable
Collection接口
Collection接口继承Iterable接口
public interface Collection<E> extends Iterable<E>
- Collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
- Collection接口没有直接的实现子类,是通过他的子接口Set和List来实现的
Collection接口常用方法,Collection01.java
接口不能实例化,以ArrayList子类来演示,Set和List都可以用
- add
- remove
- contains,查找某元素是否存在
- size
- isEmpty,是否为空
- clear,清空
- addAll
- containsAll,查找多个元素是否都存在
- removeAll
import java.util.ArrayList;
import java.util.List;
public class Collection01 {
@SuppressWarnings("all")
public static void main(String[] args) {
//add
List list = new ArrayList();
list.add("jack");
list.add(10); //list.add(new Integer(10))
list.add(true);
System.out.println(list);//[jack, 10, true]
//remove
list.remove(0);//删除第一个元素
System.out.println(list);//[10, true]
list.remove("jack");//删除指定元素
System.out.println(list);//[10, true]
//contains//是否含有某元素
System.out.println(list.contains("jack"));//false
//size
System.out.println(list.size());//2
//isEmpty
System.out.println(list.isEmpty());//false
//clear
list.clear();
System.out.println(list);//[]
//addAll
ArrayList list2 = new ArrayList();
list2.add("mary");
list2.add(20);
list.addAll(list2);
System.out.println(list);//[mary,20]
//containsAll
System.out.println(list.containsAll(list2));//true
//removeAll
list.removeAll(list2);
System.out.println(list);//[]
}
}
Collection接口遍历元素方法
- 使用Iterator迭代器,Iterator01.java
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
- Iterator仅用于遍历集合,本身并不存放对象
- while迭代快捷键:itit
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Iterator01 {
@SuppressWarnings("all")
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(new Book("三国演义", "罗贯中", 10.1));
arrayList.add(new Book("红楼梦", "曹雪芹", 5.1));
arrayList.add(new Book("西游记", "吴承恩", 15.1));
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();//返回一个Object类型的元素
System.out.println("obj=" + obj);
}
}
}
class Book {
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
}
-
for循环增强,CollectionFor01.java
简化版的迭代器,快捷键大写i
for(元素类型 元素名:集合名或数组名){
访问元素
}
import java.util.ArrayList;
public class CollectionFor01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(new Book("三国演义", "罗贯中", 10.1));
arrayList.add(new Book("红楼梦", "曹雪芹", 5.1));
arrayList.add(new Book("西游记", "吴承恩", 15.1));
for (Object book : arrayList) {
System.out.println(book);
}
}
}
- 普通for循环
for(int i=0;i<list.size(),i++){
Object o=list.get(i);
}
Iterator迭代器方法
- hasNext()
- next(),调用next方法之前要用hasNext进行检测。若不调用,且下一条记录无效,直接调用next会抛出NoSuchElementException异常
- remove()
练习,CollectionExercise.java
- 创建3个Dog{name,age}对象,放入ArrayList中,赋给List引用
- 用迭代器和增强for循环两种方式来遍历
- 重写Dog的toString方法,输出name和age
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CollectionExercise {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Dog12("d1", 10));
list.add(new Dog12("d2", 20));
list.add(new Dog12("d3", 30));
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
for (Object o : list) {
System.out.println(o);
}
}
}
class Dog12 {
private String name;
private int age;
public Dog12(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
List接口
List接口是collection接口的子接口
- List集合类中元素有序(即添加顺序和取出顺序一致),且可重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
- JDK API中List接口的实现类常用的有ArrayList、LinkedList、Vector
List接口常用方法
- void add(int index, E element);在index位置加入元素
- boolean addAll(int index, Collection c);从index位置开始加入c中元素
- Object get(int index);获取index位置元素
- int indexOf(Object o);返回o首次出现位置
- int lastIndexOf(Object o);返回o最后出现位置
- Object remove(int index);移除index位置元素并返回
- Object set(int index, E element);替换index位置的元素,返回被替换的元素
- List subList(int fromIndex, int toIndex);返回fromIndex到toIndex的子集
List练习,ListExercise.java
使用List的实现类添加三本图书,并遍历,打印如下
名称:xx 价格:xx作者:xx
名称:xx 价格:xx作者:xx
名称:xx 价格:xx作者:xx
要求:按价格排序,从低到高
要求使用ArrayList、LinkedList、Vector
结论:只要实现了List接口,那么List的实现类都可以使用List接口中的方法
import java.util.*;
@SuppressWarnings("all")
public class ListExercise {
public static void sort(List list) {
int size = list.size();
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - 1; j++) {
Book1 booki = (Book1) list.get(j);
Book1 bookj = (Book1) list.get(j + 1);
if (booki.getPrice() > bookj.getPrice()) {
list.set(j, bookj);
list.set(j + 1, booki);
}
}
}
}
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
LinkedList linkedList = new LinkedList();
Vector vector = new Vector();
arrayList.add(new Book1("三国", "罗贯中", 10.1));
arrayList.add(new Book1("三国2", "罗贯中", 220.1));
arrayList.add(new Book1("三国3", "罗贯中", 100.1));
arrayList.add(new Book1("红楼梦", "曹雪芹", 5.1));
arrayList.add(new Book1("西游记", "吴承恩", 15.1));
sort(arrayList);
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
linkedList.addAll(arrayList);
vector.addAll(arrayList);
System.out.println(arrayList);
System.out.println(linkedList);
System.out.println(vector);
}
}
class Book1 {
private String name;
private String author;
private double price;
public Book1(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return "名称:" + name + "\t\t" + "价格:" + price + "\t\t" + "作者:" + author;
}
}
ArrayList注意事项
- permits all elements,including null
- ArrayList是由数组实现数据存储的
- ArrayList基本等于Vector,但ArrayList是线程不安全,在多线程情况下不建议使用ArrayList
ArrayList底层机制,ArrayListSource.java
-
ArrayList中维护类一个Object类型的数组elementData
transient Object[] elementData;表示该属性不会被序列化
-
当创建对象时,如果使用的时无参构造器,则初始elementData容量为0
-
当添加元素时,先判断是否需要扩容,如果需要扩容,则调用grow方法,否则直接添加元素到合适位置
-
如果使用的是无参构造器,如果第一次添加,需要扩容的话,则扩容elementData为10,如果需要再次扩容的话,则扩容elementData为1.5倍
//无参构造器
public class ArrayListSource {
public static void main(String[] args) {
// public ArrayList() {
// this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
// }
// 创建了一个空的elementData数组
ArrayList list = new ArrayList();
// 使用for给list添加1-10
for (int i = 0; i < 10; i++) {
list.add(i);
}
//add
// public boolean add(E e) {
// ensureCapacityInternal(size + 1); // Increments modCount!!
// elementData[size++] = e;
// return true;
// }
// 1、先确定是否要扩容
// 2、再执行赋值操作
//ensureCapacityInternal
// private void ensureCapacityInternal(int minCapacity) {
// ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
// }
//calculateCapacity
// private static int calculateCapacity(Object[] elementData, int minCapacity) {
// //默认值DEFAULT_CAPACITY为10
// //确定是否要扩容,第一次minCapacity为10
// if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// return Math.max(DEFAULT_CAPACITY, minCapacity);
// }
// return minCapacity;
// }
//ensureExplicitCapacity
// private void ensureExplicitCapacity(int minCapacity) {
// //modCount++记录集合被修改的次数,防止多个线程同时修改
// modCount++;
// //如果elementData容量不够,进行扩容
// if (minCapacity - elementData.length > 0)//说明容量不够
// grow(minCapacity);//需要扩容
// }
//grow
// //使用扩容机制来确定要扩容到多大
// private void grow(int minCapacity) {
// int oldCapacity = elementData.length;
// //第一次newCapacity=10,之后为1.5倍
// //oldCapacity >> 1相当于/2,所以oldCapacity + (oldCapacity >> 1)为1.5
// int newCapacity = oldCapacity + (oldCapacity >> 1);
// if (newCapacity - minCapacity < 0)
// newCapacity = minCapacity;
// if (newCapacity - MAX_ARRAY_SIZE > 0)
// newCapacity = hugeCapacity(minCapacity);
// //扩容使用Arrays.copyOf
// //为什么用copyOf,会保留原先数据,再增加
// elementData = Arrays.copyOf(elementData, newCapacity);
// }
//使用for给list添加11-15,容量为15
for (int i = 11; i < 15; i++) {
list.add(i);
}
list.add(100);//容量为22
list.add(200);
list.add(null);
}
}
- 如果使用指定容量capacity的构造器,则初始elementData容量为capacity
- 如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容elementData为1.5倍
Vector底层结构和源码,Vector01.java
和ArrayList扩容机制类似
import java.util.Vector;
@SuppressWarnings("all")
public class Vector01 {
public static void main(String[] args) {
Vector vector = new Vector();
for (int i = 0; i < 10; i++) {
vector.add(i);
}
// public synchronized boolean add(E e) {
// modCount++;
// //确定是否需要扩容ensureCapacityHelper
// ensureCapacityHelper(elementCount + 1);
// elementData[elementCount++] = e;
// return true;
// }
// private void ensureCapacityHelper(int minCapacity) {
// if (minCapacity - elementData.length > 0)
// grow(minCapacity);
// }
// private void grow(int minCapacity) {
// int oldCapacity = elementData.length;
// //扩容容量((capacityIncrement > 0) ?capacityIncrement : oldCapacity)
// //如果capacityIncrement>0,相当于翻2倍
// int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
// capacityIncrement : oldCapacity);
// if (newCapacity - minCapacity < 0)
// newCapacity = minCapacity;
// if (newCapacity - MAX_ARRAY_SIZE > 0)
// newCapacity = hugeCapacity(minCapacity);
// elementData = Arrays.copyOf(elementData, newCapacity);
// }
vector.add(100);
}
}
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
protected Object[] elementData;
}
-
Vector底层也是一个对象数组
-
Vector是线程同步的,即线程安全,方法带有synchronized
开发中需要线程同步安全时,考虑使用Vector
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
LinkedList,LinkedList01.java
- LinkedList底层实现了双向链表和双端队列特点
- 可以添加任意元素,包括null
- 线程不安全,没有实现同步
双向链表
public class LinkedList01 {
public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node mary = new Node("mary");
//链接三个节点,形成双向链表
jack.next = tom;
tom.next = mary;
mary.pre = tom;
tom.pre = jack;
Node first = jack;//双向链表的首节点
Node last = mary;//双向链表的尾结点
//从头到尾遍历
while(true) {
if (first == null) {
break;
}
//输出first
System.out.println(first);
first = first.next;
}
//从尾到头遍历
while(true){
if (last==null){
break;
}
//输出first
System.out.println(last);
last = last.next;
}
}
}
class Node{
public Object item; //存数据
public Node next;//后一个节点
public Node pre;//前一个节点
public Node(Object item) {
this.item = item;
}
@Override
public String toString() {
return "node name=" + item;
}
}
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中prev指向前一个,next指向后一个,实现双向链表
- 元素的删除和添加效率高
add源码,LinkedList02.java
import java.util.LinkedList;
public class LinkedList02 {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add(1);
// public boolean add(E e) {
// linkLast(e);
// return true;
// }
//
// void linkLast(E e) {
// final LinkedList.Node<E> l = last;
// final LinkedList.Node<E> newNode = new LinkedList.Node<>(l, e, null);
// last = newNode;
// if (l == null)
// first = newNode;
// else
// l.next = newNode;
// size++;
// modCount++; v
// }
//
// private static class Node<E> {
// E item;
// LinkedList.Node<E> next;
// LinkedList.Node<E> prev;
//
// Node(LinkedList.Node<E> prev, E element, LinkedList.Node<E> next) {
// this.item = element;
// this.next = next;
// this.prev = prev;
// }
// }
}
}
ArrayList、Vector、LinkedList比较
底层结构 | 版本 | 线程安全效率 | 扩容倍数 | |
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 1.5倍 |
Vector | 可变数组 | jdk1.0 | 安全,效率不高 | 2倍 |
LinkedList | 双向链表 | 线程不安全,增删效率高,改查效率低 |
- 如果改查操作多,选择ArrayList
- 如果增删操作多,选择LinkedList
Set
- 无序取出的顺序和添加顺序不一致,但是是固定的顺序,没有索引
- 不允许重复元素,所以最多包含一个null
Set接口常用方法
同Collection接口
遍历方式
- 迭代器
- 增强for
- 不能使用索引的方式
HashSet
- 实现了Set接口
- HashSet实际上是HashMap
- 可以存放null,但只能有一个null
- HashSet不保证元素是有序的,取决于hash后,再确定索引的结果
- 不能有重复元素
HashSet底层机制,HashSet01.java
HashSet底层是HashMap,HashMap底层是数组+链表+红黑树
- 添加一个元素时,先得到hash值,会转成索引值
- 找到存储数据表table,看这个索引位置是否有存放的元素
- 如果没有,直接加入
- 如果有,调用equals比较,如果相同,放弃添加,如果不同,则添加到最后
- Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认8),并且table大小≥MIN_TREEIFY_CAPACITY(默认64),就会进行红黑树化
import java.util.*;
@SuppressWarnings("all")
public class HashSet01 {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add("aaa");
hashSet.add("bbb");
hashSet.add("ccc");
//add
// public boolean add(E e) {
// //返回null,代表添加成功
// return map.put(e, PRESENT)==null;
// }
//put
// public V put(K key, V value) {
// return putVal(hash(key), key, value, false, true);
// }
//hash
// static final int hash(Object key) {
// int h;
// //无符号右移16位得到hash值
// return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
// }
//putVal
// final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
// HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;//辅助变量
// //table是放node节点的数组,是hashMap的属性
// //如果table为空,第一次扩容,16个空间,1<<4
// if ((tab = table) == null || (n = tab.length) == 0)
// n = (tab = resize()).length;
// //根据key,得到hash去计算key放到table的哪个位置
// //并把这个位置的对象赋给p
// //判断p是否为空,如果为空,表示还没存放数据,创建一个Node,放在该位置
// if ((p = tab[i = (n - 1) & hash]) == null)
// tab[i] = newNode(hash, key, value, null);
// else {
// HashMap.Node<K,V> e; K k;//辅助变量
// //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
// //并且满足以下两个条件之一
// //1、准备加入的key 和 p指向的node节点的key 是同一个对象
// //2、准备加入的key 用程序员编写的equals()方法比较 p指向的node节点的key
// //则不能加入
// if (p.hash == hash &&
// ((k = p.key) == key || (key != null && key.equals(k))))
// e = p;
// //判断p是不是一棵红黑树,是的话调用putTreeVal
// else if (p instanceof HashMap.TreeNode)
// e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
// //如果table对应索引位置已经是一个链表,使用for循环比较,
// //1、依次和该链表的每一个元素比较后,都不相同,则加入到该链表的最后
// // 如果链表达到8个节点,table大小<64,就调用treeifyBin将链表进行扩容
// // 如果链表达到8个节点,table大小>64不满足上述条件,则进行树化
// //2、如果有相同情况,直接break
// else {
// for (int binCount = 0; ; ++binCount) {
// if ((e = p.next) == null) {
// p.next = newNode(hash, key, value, null);
// 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;
// //每加入一个节点,不管是table中还是链表上,size就会加1
// if (++size > threshold)
// resize();
// //HashMap空方法
// afterNodeInsertion(evict);
// return null;
// }
}
}
HashSet的扩容和转成红黑树机制
- HashSet底层是HashMap,第一次添加时table数组扩容到16,临界值threshold(16)*加载因子loadFactor(0.75)=12
- 如果table数组使用到了临界值12,就会扩容到162=32,新的临界值就是320.75=24,以此类推
- Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认8),并且table大小≥MIN_TREEIFY_CAPACITY(默认64),就会进行红黑树化,否则仍然采用数组扩容机制
LinkedHashSet
- 是HashSet的子类
- 底层是LinkedHashMap,底层维护了一个数组+双向链表
- LinkedHashSet根据hashCode决定元素的存储位置,同时用链表维护元素的次序,使得元素看起来是以插入顺序保存的
- 不允许添加重复元素
说明:
- LinkedHashSet维护了一个hash表和双向链表
- 每个节点都有pre和next
- 在添加元素时先求hash值,再求索引,确定该元素在table的位置,然后添加元素到双向链表
- 这样能确保插入顺序和遍历顺序一致
Map接口,Map01.java
- Map与Collection并列存在,用于保存具有映射关系的操作,key-value
- Map中的key和value可以是任意引用类型的数据,会封装到HashMap$Node对象中
- Map中的Key不允许重复,value可以重复
- key可以为null,但只能有一个,value也可以为null
- 常用String作为key
- key和value之间存在一对一关系,即通过指定key总能找到对应的value
- 两个元素key相同时,value替换
- 一对k-v放在一个Node中,因为Node实现了Entry接口
import java.util.*;
public class Map01 {
public static void main(String[] args) {
Map map = new HashMap();
map.put("key","value");
map.put("1","2");
System.out.println(map);//{1=2, key=value}
}
}
内部
- key-value最后是HashMap$Node node= newNode(hash,key,value,null)
- k-v为了方便程序员的遍历,会创建EntrySet集合,该将元素存放进Entry类,
- Entry类:EntrySet<Entry<K,V>>,即transient Set<Map.Entry<K,V>> entrySet
- entrySet中,定义的类型是Map.Entry,但实际上存放的还是HashMap$Node,这是因为static class Node<K,V> implements Map.Entry<K,V>
- 当把HashMap$Node对象存放到entrySet,就方便我们遍历,因为Map.Entry提供了getSet()获取key的Set和getValue()获取value的Collection
Map接口方法
- put,添加
- remove,根据key删除映射关系
- get,根据key获取值
- size,获取元素个数
- isEmpty,判断元素个数是否为0
- clear,清除
- containsKey,查找key是否存在
Map遍历方法,MapFor01.java
import java.util.*;
@SuppressWarnings("all")
public class MapFor01 {
public static void main(String[] args) {
Map map = new HashMap();
map.put("1","A");
map.put("3","B");
map.put("2","C");
map.put("4","D");
map.put("5","#");
//第一组:先取出所有key,通过key取出对应的value 返回此映射中包含的键的 set 视图。
Set keySet = map.keySet();
//增强for
for (Object key :keySet) {
// System.out.println(key+"-"+map.get(key));
}
//迭代器
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
// System.out.println(key+"-"+map.get(key));
}
//第二组:取所有values
Collection values = map.values();
//Collection所有三种遍历方式
//第三组:通过EntrySet来获取 返回此映射中包含的映射关系的 set 视图。
Set entryset = map.entrySet();
for (Object entry :entryset) {
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey()+"-"+m.getValue());
}
Iterator iterator1 = entryset.iterator();
while (iterator1.hasNext()) {
Object entry = iterator1.next();
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey()+"-"+m.getValue());
}
}
}
Map练习,MapExercise.java
使用HashMap添加3个员工对象,要求
- 键:员工id
- 值:员工对象
- 并遍历显示工资>8000的员工
- 员工类:姓名、工资、员工id
import java.util.*;
public class MapExercise {
public static void main(String[] args) {
Map hashMap = new HashMap();
hashMap.put(1,new Employee(1,"张1",5000));
hashMap.put(2,new Employee(2,"张2",10000));
hashMap.put(3,new Employee(3,"张3",15000));
for (Object o :hashMap.keySet()) {
Employee e = (Employee) hashMap.get(o);
if (e.salary>8000){
System.out.println(e.name+" "+e.salary);
}
}
Set entrySet = hashMap.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
Map.Entry entry = (Map.Entry) next;
Employee e = (Employee)entry.getValue();
if(e.salary>8000){
System.out.println(e.name+" "+e.salary);
}
}
}
}
class Employee{
public int id;
public String name;
public double salary;
public Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
}
HashMap扩容机制
- HashMap底层维护了Node类型的数组table,默认为null
- 当创建对象时,将加载因子(loadfactor)初始化为0.75
- 当添加key-val时,通过key的哈希值得到table的索引,然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等,如果相等,直接替换val,如果不等需要判断是树结构还是链表结构,作出相应处理
- 第一次添加,需要扩容table到16,临界值(threshold)为12
- 以后再扩容,容量会变成原来的两倍
HashTable
- 存放k-v
- k-v都不能为null
- hashTable是线程安全的,HashMap是线程不安全的
Properties
- 继承自HashTable类并实现了Map接口,也是使用键值对保存数据
- Properties可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
TreeMap
重写compare方法
@Override
public int compare(Object o1,Object o2){
return ((String) o1).compareTo((String) o2);
}
总结
开发中如何选择集合实现类
- 判断存储类型
- 一组对象:Collection接口
- 允许重复:List
- 增删多:LinkedList(底层维护了一个双向链表)
- 查改多:ArrayList(底层维护Object类型的可变数组)
- 不允许重复:Set
- 无序:HashSet(底层是HashMap,维护一个哈希表(数组+链表+红黑树))
- 排序:TreeSet,(底层是TreeMap),重写compare方法
- 插入和取出顺序一致:LinkedHashSet,维护(数组+双向链表)
- 允许重复:List
- 一组键值对:Map接口
- 键无序:HashMap(底层是哈希表(数组+链表+红黑树))
- 键排序:TreeMap
- 键插入和取出顺序一致:LinkedHashMap
- 读取文件:Properties
- 一组对象:Collection接口
Collections工具类
- Collections是一个操作Set、List、Map等集合的工具类
- 提供了一系列静态方法对集合元素进行排序、查询和修改操作
- 排序方法:
- reverse(List):反转list元素
- shuffle(List):随机排序list元素
- sort(List):升序排序
- sort(List,Comparator):根据指定Comparator的顺序对list进行排序
- 排序方法:
Collections.sort(list,new Comparator(){
@Override
public int compare(Object o1,Object o2){
return ((String)o1).length()-((String)o2).length();
}
}
1. swap(List,int i,int j):交换list中i和j的位置
1. 查找、替换方法:
1. max(Collection):最大值
2. max(Collection, Comparator):根据Comparator指定顺序返回给定集合中最大值
3. min(Collection)
4. min(Collection,Comparator)
5. frequency(Collection,Object):返回集合中指定元素的出现次数
6. copy(List dest,List src):将src的内容复制到dest中
7. replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象所有旧值
章节作业
SetHomework01.java
要求:
- 封装一个新闻类,包含标题和内容属性提供get、 set方法,重写toString方法,打印对象时只打印标题;
- 只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:
新闻一:新冠确诊病例超千万,数百万印度教信徒赴恒河 “圣浴”,引民众担忧。
新闻二:男子突然想起2个月前钓的鱼还在往兜里,一看赶紧放生。 - 将新闻对象添加到Arraylist集合中,并进行倒序遍历
- 在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前15个,然后在后边加"…"
- 在控制台打印遍历出经过处理的新闻标题;
import java.util.*;
public class SetHomework01 {
public static String processTitle(String title) {
if (title == null) {
return "";
}
if (title.length() > 15) {
return title.substring(0, 16) + "...";
} else {
return title;
}
}
public static void main(String[] args) {
// 1. 封装一个新闻类,包含标题和内容属性提供get、 set方法,重写toString方法,打印对象时只打印标题;
// 2. 只提供一个带参数的构造器,实例化对象时,只初始化标题;并且实例化两个对象:
// 新闻一:新冠确诊病例超千万,数百万印度教信徒赴恒河 “圣浴”,引民众担忧。
// 新闻二:男子突然想起2个月前钓的鱼还在往兜里,一看赶紧放生。
// 3. 将新闻对象添加到Arraylist集合中,并进行倒序遍历
// 4. 在遍历集合过程中,对新闻标题进行处理,超过15字的只保留前15个,然后在后边加"..."
// 5. 在控制台打印遍历出经过处理的新闻标题;
News news1 = new News("新冠确诊病例超千万,数百万印度教信徒赴恒河 “圣浴”,引民众担忧");
News news2 = new News("男子突然想起2个月前钓的鱼还在往兜里,一看赶紧放生");
ArrayList arrayList = new ArrayList();
arrayList.add(news1);
arrayList.add(news2);
for (int i = arrayList.size() - 1; i >= 0; i--) {
News news = (News) arrayList.get(i);
System.out.println(processTitle(news.getTitle()));
}
}
}
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 "title=" + title;
}
}
HashSet和TreeSet如何去重
- HashSet的去重机制:hashCode()+equals(),底层通过存入对象,进行运算得到一个hash值,通过hash值对应的索引,如果发现table索引所在位置,没有数据,就直接存放,如果有数据,就进行equals遍历比较,如果比较后,不相同就加入,否则不加入。
- TreeSet去重机制:如果传入了一个Comparator匿名对象,就使用实现的compare方法去重,如果方法返回0,就认为是相同的数据,就不添加。如果没有传入Comparator则以添加的对象实现的Comparable接口的compareTo去重