数组&集合
数组 : 存储多个数据
引用数据类型,对象数据
长度一旦确定不可改变,定长的
存储的多个数据要求数据类型相同
索引,有序的
集合|容器 : 存储多个数据
可以根据数据的增删动态改变长度
可以存储不同类型的数据
要求集合只能存储引用数据类型的数据
集合的继承体系
一,Collection :
集合层次结构中的根接口。 集合表示一组对象,称为其元素 。
List子接口下的实现类有序可重复,Set子接口下的实现类无序不可重.
1.遍历方式 :
1.foreach
2.iterator迭代器
//foreach
for(Object obj:col1){
System.out.println(obj);
}
//iterator迭代器
//1)获取迭代器对象
Iterator it = col1.iterator();
//2)判断是否存在下一个元素
while(it.hasNext()){
//3)获取下一个元素
System.out.println(it.next());
}
2. 子接口:List :
有序的可重复
2.1新增方法 :
新增了一些根据索引进行操作的方法
//void add(int index, E element) 将指定元素插入此列表中的指定位置(可选操作)。
list.add(3,"ddd");
System.out.println(list);
//boolean addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到指定位置的此列表中(可选操作)。
//E get(int index) 返回此列表中指定位置的元素。
System.out.println(list.get(1));
//int indexOf(Object o) 返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。
System.out.println(list.indexOf("aaa"));
//int lastIndexOf(Object o) 返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回-1。
System.out.println(list.lastIndexOf("aaa"));
//static <E> List<E> of(E... elements) 返回包含任意数量元素的不可修改列表。
List list2 = List.of(1,2,3,4,5);
System.out.println(list2);
//list2.add(6); //java.lang.UnsupportedOperationException
System.out.println(list2);
//E remove(int index) 删除此列表中指定位置的元素(可选操作)。
System.out.println(list.remove("ddd"));
System.out.println(list.remove(2));
System.out.println(list);
//boolean remove(Object o) 从该列表中删除指定元素的第一个匹配项(如果存在)(可选操作)。
//E set(int index, E element) 用指定的元素替换此列表中指定位置的元素(可选操作)。
System.out.println(list.set(1,"bbbb"));
System.out.println(list);
//List<E> subList(int fromIndex, int toIndex) 返回指定的 fromIndex (包含)和 toIndex (不包括)之间的此列表部分的视图。
System.out.println(list.subList(0,2));
//for
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
遍历方式 :
普通for
增强for
Iterator迭代器
ListIterator列表迭代器
List list = new ArrayList();
list.add("钢铁侠");
list.add("美国队长");
list.add("黑寡妇");
list.add("灭霸");
list.add("猩红女巫");
System.out.println(list);
//1.contains
/*if(list.contains("灭霸")){
list.add("惊奇队长");
}*/
//2.for
/*for(int i=0;i<list.size();i++){
if("灭霸".equals(list.get(i))){
list.add("惊奇队长");
}
}*/
//3.foreach
/*for(Object obj:list){ //java.util.ConcurrentModificationException
if("灭霸".equals(obj)){
list.add("惊奇队长");
}
}*/
//4.iterator
/*Iterator it1 = list.iterator(); //java.util.ConcurrentModificationException
while(it1.hasNext()){
if("灭霸".equals(it1.next())){
list.add("惊奇队长");
}
}*/
//ListIterator<E> listIterator() 返回此列表中元素的列表迭代器(按适当顺序)。
//ListIterator<E> listIterator(int index) 从列表中的指定位置开始,返回列表中元素的列表迭代器(按正确顺序)。
//1)获取迭代器对象
ListIterator it2 = list.listIterator();
//2)判断是否存在下一个元素|是否存在上一个元素
while (it2.hasNext()){
//3)获取下一个元素|获取上一个元素
//System.out.println(it2.nextIndex()+"------->"+it2.next());
if("灭霸".equals(it2.next())){
it2.add("惊奇队长");
}
}
/*while (it2.hasPrevious()) {
System.out.println(it2.previousIndex()+"---->"+it2.previous());
}*/
System.out.println(list);
2.2泛型 :
jdk1.5新特性
参数化类型 : 数据类型作为参数传递
语法 : <数据类型>
位置 :
泛型类 : 定义类型的时候,类型后<T>,<E>...
泛型方法 : 方法上使用泛型
特点 :
发生在编译期间,运行期间泛型无效-->泛型擦除
泛型只能传递引用数据类型
优点 :
1.使用灵活,避免类型转换异常的出现
2.增强程序的稳定性与可读性
3.便于后期维护
Collection<Integer> list = new ArrayList<>();
2.3 ArrayList 实现类:
有序,可重复,可以根据索引进行操作
List接口的可调整大小的阵列实现。 实现所有可选列表操作,并允许所有元素,包括null 。
这个类大致相当于Vector ,除了它是不同步的。
底层结构 : 可变数组 Object[] elementData;
特点 : 根据索引查询效率高,做增删效率低,因为大量的涉及到新数组创建,数据的拷贝
引用场景 : 适合应用在大量做查询,少量做增删的场景
初始容量 : 10 DEFAULT_CAPACITY = 10
扩容机制 : 通过Arrays.copyOf方法实现创建新数组拷贝数据,每次扩容原容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1);
新增方法 : 无
遍历方式 :
1.for
2.foreach
3.Iterator
4.ListIterator
注意 :
如果某个类型底层结构为数组或数组相关类型,需要关注初始容量与扩容机制
//void forEach(Consumer<? super E> action) 对 Iterable每个元素执行给定操作,直到处理 Iterable所有元素或操作引发异常。
list.forEach(System.out::println);
练习 : 定义ArrayList集合,存储学生数据,测试操作
2.4Vector 向量
ArrayList与Vector区别 :
共同点 : 都是List接口的实现类,有序可重复的
底层结构都是数组,查询效率高,增删效率低
不同点 :
线程安全|同步问题 :
ArrayList线程不安全|不同步
Vector 线程安全|同步
如果不需要线程安全实现,建议使用ArrayList代替Vector 。
初始容量与扩容机制 :
ArrayList 初始容量默认10|可以在构造器中指定,在add方法中创建
Vector 初始容量默认10|可以在构造器中指定,在构造器中直接创建
ArrayList 每次扩容原容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity>>1);
Vector 默认每次扩容原容量的2倍|可以根据需求指定每次扩容多少-->容量增量
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
2.5 LinkedList : 有序的,可重复的
实现所有可选列表操作,并允许所有元素(包括null )。
底层结构 : 双向链表,使用头插法进行增加元素
链表:
以节点为单位
单向链表:记录链表头的节点
双向链表:记录链表头与链表尾节点
特点 : 查询效率低,增删效率高
应用场景: 适合应用在大量做增删,少量做查询的情况
新增方法 : 新增了一些操作链表头尾的方法
遍历方式 : 1)for 2)foreach 3)iterator 4)listIterator
//void addFirst(E e) 在此列表的开头插入指定的元素。
list.addFirst(null);
//void addLast(E e) 将指定的元素追加到此列表的末尾。
list.addLast("ddd");
System.out.println(list);
3.子接口 Set :
无序的,不可重复(去重的)
无序 : 存放的顺序与内部真实存储的顺序不保证一致
新增方法 : static <E> Set<E> of(E... elements) 返回包含任意数量元素的不可修改集。
Set<Character> set2 = Set.of('c','a','b','d');
System.out.println(set2);
//set2.remove('a'); //java.lang.UnsupportedOperationException
//System.out.println(set2);
遍历方式 :
foreach
iterator
//foreach
for (Character ch :
set2) {
System.out.println(ch);
}
//使用iterator迭代器进行遍历
Iterator it=set2.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
3.1 TreeSet实现类 : 底层是由TreeMap维护的
无序的,不可重的
底层结构 : 红黑树(平衡二叉树)
特点 : 查询效率高,去重,默认升序排序
//创建一个TreeSet
TreeSet<Integer> set=new TreeSet<>();
//测试是否可以实现升序排序
set.add(1);
set.add(3);
set.add(2);
set.add(5);
set.add(4);
System.out.println(set);//[1, 2, 3, 4, 5]
//测试是否可以实现降重
set.add(4);
System.out.println(set);//[1, 2, 3, 4, 5]
引用场景 : 适合应用在存储多个单个值的数据的集合,去重的,自动升序排序的场景
3.1.1新增方法 : 新增了一些与比较大小相关的方法
//E ceiling(E e) 返回此set中大于或等于给定元素的最小数据,如果没有这样的元素,则 null 。
System.out.println(set.ceiling(2.2)); //2.2
//E floor(E e) 返回此set中小于或等于给定元素的最大元素,如果没有这样的元素,则 null 。
System.out.println(set.floor(3.1));
//E first() 返回此集合中当前的第一个(最低)元素。
System.out.println(set.first());
//E last() 返回此集合中当前的最后一个(最高)元素。
System.out.println(set.last());
//E higher(E e) 返回此集合中的最小元素严格大于给定元素,如果没有这样的元素,则 null 。
System.out.println(set.higher(2.2)); //3.2
//E lower(E e) 返回此集合中的最大元素严格小于给定元素,如果没有这样的元素,则 null 。
//E pollFirst() 检索并删除第一个(最低)元素,如果此组为空,则返回 null 。
//E pollLast() 检索并删除最后一个(最高)元素,如果此集合为空,则返回 null 。
3.1.2 遍历方式 : 1)foreach 2)iterator
3.1.3 注意
测试TreeSet是否能够同时存储不同类型的数据 : java.lang.ClassCastException 不能存储多个不同类型数据,因为不是同种类型数据无法比较大小,无法升序排序
定义TreeSet存储自定义User类型的数据 : java.lang.ClassCastException
3.2 查找TreeSet集合中存储的类型数据的比较规则
1)定义TreeSet时候,构造器中通过参数传递比较规则-->外部比较规则
2)检查存储的数据类型是否存在默认的比较规则,如果存在使用,如果还不存在,就抛出异常 -->内部比较规则
//TreeSet<User> set = new TreeSet<>(); 根据数据的默认比较规则比较大小进行升序 排序,如果没有默认比较规则就报错
//TreeSet(Comparator<? super E> comparator) 构造一个新的空树集,根据指定的比较器进行排序。
TreeSet<User> set = new TreeSet<>(new Demo());
//实现类--> 就是用来定义User类型数据的外部外部比较器 : 根据用户名做升序排序
class Demo implements Comparator<User>{
@Override
public int compare(User o1, User o2) {
return o2.getName().compareTo(o1.getName());
}
}
3.2.1比较器|比较规则 :
内部比较器|内部比较规则|自然排序 : 实体类实现java.lang.Comparable接口,重写int compareTo(T o) 方法,方法的内部定义比较规则
int compareTo(T o) : x.compareTo(y)
返回值 : 比较的结果
0 --> x==y
<0 --> x<y
>0 --> x>y
//重写的compareTo方法。。。内部比较器
@Override
public int compareTo(User o) {
//this o
return this.num -o.num;
}
3.2.2外部比较器|外部比较规则|定值排序 :
实现类实现Comparator接口重写compare方法,方法内部定义比较规则
//实现类--> 就是用来定义User类型数据的外部外部比较器 : 根据用户名做升序排序
class Demo implements Comparator<User>{
@Override
public int compare(User o1, User o2) {
return o2.getName().compareTo(o1.getName());
}
}
3.2.3注意 :
TreeSet集合存储自定义的引用数据类型数据,去重与升序排序都是根据比较规则(比较器)
需要自己实现内部或外部比较器,根据实际情况选择
3.3 HashSet实现类 :
无序的,不可重复的
底层是由HashMap维护
注意:虽然jdk1.8之后,底层结构有红黑树的参与,红黑树是有序的但是红黑树的使用只是在单个索引的,整体的存储任然是无序的
存储过程:
要存储的数据调用hashCode方法得到整数值
int code=对象数据.hashCode();
整数调用hashCode方法等到的就是整数本身的值
把第一步code值进一步通过指定的hash算法计算出位桶的索引(哈希表结构数组的索引)
int index=code%数组长度;
判断节点数组[index] 是否存在数据(arr[index]==null)
如果不存在,证明没有单向链表的首节点,创建新节点,作为单项链表的首节点放入位桶当中
如果存在节点,这个节点就是单向链表的首节点,遍历这个链表,获取到每一个节点存储的数据值调用equals方法要与存储的数据值比较是否相等,相等就去重(不存储),如果的所有的都不相等,创建新节点放在原单向链表的最后
3.3.1 底层结构 :
哈希表(jdk1.8之前 : 数组+链表 jdk1.8及之后 : 数组+链表+红黑树)
hashCode:返回对象的整数表现形式
Object类中的实现:根据对象地址计算返回整数值
重写的hashCode方法:根据对象的所有属性值计算返回的整数
3.3.2 特点 :
查询增删效率较高(此处的效率较高,是相对而言的,Arraylist效率更高,但是再实际应用当中我们会用到去重的功能
3.3.3 应用场景 :
去重的需求下,查询增删相对效率较高的集合类型适合使用HashSet
3.3.4 新增功能 :
无新增方法
3.3.5 遍历方式 :
1)foreach 2)iterator
去重 : 要求重写哈希表存储的数据类型的hashCode与equals方法,实现根据所有属性值重写,而非根据地址比较计算
重写hashCode与equals方法的前提下 : 根据所有属性值重写
equals返回值相等两个对象,调用hashCode肯定相等(相等才能在同一个桶中,才有机会调用equals比较去重)
hashCode返回值相等,equals不一定相等
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
二,Map :
键值对的集合| 映射的集合
键值对的key的唯一的,去重的,无序的 --- > Set
键值对的value,可重复,无序的 --- > Collection
一个key只能对应一个value
去重 : key相同value覆盖
Map : 方法
map.put(1001,"张三");
map.put(1003,"王五");
map.put(1004,"老薛");
map.put(1002,"Lisa");
System.out.println(map);//{1001=张三, 1002=Lisa, 1003=王五, 1004=老薛}
System.out.println( map.put(1005,"lucy"));; //null
System.out.println(map);//{1001=张三, 1002=Lisa, 1003=王五, 1004=老薛, 1005=lucy}
System.out.println((map.put(1005,"卢西"))); //lucy
1.Map 遍历方式 :
1.1 Collection<V> values() 返回此映射中包含的值的Collection视图。
1.2 Set<K> keySet() 返回此映射中包含的键的Set视图。
1.3 Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射的Set视图。
Map<Integer,String> map = new HashMap<>();
map.put(1001,"张三");
map.put(1003,"王五");
map.put(1004,"老薛");
map.put(1002,"Lisa");
map.put(1005,"Lisa");
System.out.println("----------------values()----------------");
Collection<String> values = map.values();
for(String str:values){
System.out.println(str);
}
System.out.println("-------------keySet()-----------------");
Set<Integer> keys = map.keySet();
Iterator<Integer> it = keys.iterator();
while(it.hasNext()){
Integer key = it.next();
String value = map.get(key);
System.out.println(key+"--->"+value);
}
System.out.println("-------------entrySet()-----------------");
Set<Map.Entry<Integer,String>> entrys = map.entrySet();
for (Map.Entry<Integer,String> entry:entrys){
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
2.TreeMap实现类 :
存储键值对数据,key是唯一的,去重的,无序的(存放的顺序与内部真实存储的顺序不保证一致)
底层结构 : 红黑树
特点 : 根据key做自动升序排序(比较器)
去重与升序排序 : 根据key比较规则做去重与排序
//TreeMap<String,Double> map = new TreeMap<>(); 使用key的类型的默认比较器进行对键值对key做比较大小,做升序排序
//TreeMap(Comparator<? super K> comparator) 构造一个新的空树图,根据给定的比较器排序。
//TreeMap<Person,Double> map = new TreeMap<>(new Impl()); //根据参数传递的比较规则对key做比较去重与排序
2.1新增方法 :
新增了一些与比较大小相关的方法
//Map.Entry<K,V> ceilingEntry(K key) 返回与大于或等于给定键的最小键关联的键 - 值映射,如果没有此键,则 null 。
//K ceilingKey(K key) 返回大于或等于给定键的 null键,如果没有这样的键,则 null 。
System.out.println(map.ceilingKey(new Person("lisi",27)));
System.out.println(map.get(map.ceilingKey(new Person("lisi",27))));
//Map.Entry<K,V> floorEntry(K key) 返回与小于或等于给定键的最大键关联的键 - 值映射,如果没有此键,则 null 。
//K floorKey(K key) 返回小于或等于给定键的最大键,如果没有这样的键,则 null 。
System.out.println("--->"+map.floorKey(new Person("lisi",27)));
//Map.Entry<K,V> lowerEntry(K key) 返回与严格小于给定键的最大键相关联的键 - 值映射,如果没有这样的键,则 null 。
//K lowerKey(K key) 返回严格小于给定键的最大键,如果没有这样键,则返回 null 。
System.out.println("--->"+map.lowerKey(new Person("lisi",27)));
//Map.Entry<K,V> firstEntry() 返回与此映射中的最小键关联的键 - 值映射,如果映射为空,则 null 。
//K firstKey() 返回此映射中当前的第一个(最低)键。
System.out.println(map.firstKey());
//Map.Entry<K,V> lastEntry() 返回与此映射中的最大键关联的键 - 值映射,如果映射为空,则 null 。
//K lastKey() 返回此映射中当前的最后一个(最高)键。
2.2注意 :
Map集合下所有的实现类,去重|排序都是根据key实现的,与value无关
练习 :
定义TreeMap集合存储键值对数据
键为Double类型,值为String类型,测试使用
键为Employee类型,值为员工所在公司名字,测试使用
3. HashMap 实现类:
基于哈希表的Map接口的实现。 此实现提供了所有可选的映射操作,并允许null值和null键。
底层结构 : 哈希表(jdk8之前:数组+链表 jdk8及之后:数组+链表+红黑树)
特点 : 查询增删效率高,去重根据键值对的key实现
应用场景 : 适合应用在存储多个键值对数据的情况下,查询增删效率高应用场景
新增方法 : 无新增方法
遍历方式 : 1)values() 2)keySet() 3)entrySet()
初始容量 :默认static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; --> 可以通过构造器指定
加载因子 : 默认static final float DEFAULT_LOAD_FACTOR = 0.75f; --> 可以通过构造器指定
扩容阈值 : threshold 存储数据的个数size > 数组的容量*加载因子
扩容机制 : 每次扩容原容量的2倍 newCap = oldCap << 1
哈希表中存储键值对的个数 : size
存储过程 :
1.调用put方法添加键值对key,value数据
2.根据key计算hash值
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
3.调用putVal(hash,key,value)实现存储键值对数据
4.执行putVal方法,第一步就判断哈希表底层的节点数组是否==null,或者底层数组的长度==0,如果是证明这是第一次添加,底层没有数组或者没有带有容量的数组,先调用resize()方法实现创建新数组
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
5.根据key的hash值计算位桶索引int index = (n - 1) & hash,判断table[index]==null是否存在单向链表的首节点
if ((p = tab[i = (n - 1) & hash]) == null)
6.如果=null,证明不存在单向链表,直接创建新节点,存储这一次要添加的键值对数据,直接放在数组table[index]作为单向链表的首节点
tab[i] = newNode(hash, key, value, null); --> new Node<>(hash, key, value, next)
7.如果!=null,证明存在单向链表的首节点,遍历这个链表,判断链表中每一个节点的存储的key与这一次要添加的键值对的key比较是否相等,
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
如果相同value覆盖
V oldValue = e.value;
e.value = value;
return oldValue;
如果不相同创建新节点,挂在原单向链表的最后
p.next = newNode(hash, key, value, null);
8.如果以上的步骤中执行到了new Node()创建新节点放入哈希表中,数据个数+1,判断是否>扩容阈值threshold,如果满足,就调用resize方法实现扩容
if (++size > threshold)
resize();
注意 :
哈希表底层节点数组的长度为2的整数次幂
当单向链表的节点个数>=8(static final int TREEIFY_THRESHOLD = 8;),并且同时哈希表的节点数组的容量>=64(static final int MIN_TREEIFY_CAPACITY = 64;),就把单向链表优化成红黑树
但是如果数组的容量<64,就调用resize()实现数组的扩容
4. HashTable
4.1 Hashtable 与 HashMap之间区别 :
共同点 :
都是Map接口的实现类,都存储键值对的数据,底层都是哈希表,特点与应用场景类似
异同点 :
1.继承体系不同
Hashtable-->父类-->java.util.Dictionary<K,V>
HashMap -->父类-->java.util.AbstractMap<K,V>
2.线程安全|不同问题
HashMap : 线程不安全|不同步的,但是效率相对较高
Hashtable : 线程安全|同步,但是效率相对较低
3.null值的处理不同
Hashtable : 任何非null对象都可以用作键或值。
HashMap : 并允许null值和null键。
4.初始容量与扩容机制不同
初始容量 :
Hashtable : 11
HashMap : 16
扩容机制
Hashtable : 每次扩容原容量的2倍+1 int newCapacity = (oldCapacity << 1) + 1;
HashMap : 每次扩容原容量的2倍 newCap = oldCap << 1
5.计算hash值与位桶索引index的方式不同
Hashtable :
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
HashMap :
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
int index = (n - 1) & hash;
5.Collections :
void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
void shuffle(List) //对List容器内的元素进行随机排列
void reverse(List) //对List容器内的元素进行逆续排列
void fill(List, Object) //用一个特定的对象重写整个List容器
int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
List<Integer> list = Arrays.asList(3,1,5,2,4);
System.out.println(list);
//void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
Collections.sort(list);
System.out.println(list);
//void shuffle(List) //对List容器内的元素进行随机排列
Collections.shuffle(list);
System.out.println(list);
//void reverse(List) //对List容器内的元素进行逆续排列
Collections.reverse(list);
System.out.println(list);
//int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
//先要求升序排序
Collections.sort(list);
System.out.println(Collections.binarySearch(list,14)); //-6 -插入点-1
ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
map.put("haha",123);
map.put("hehe",234);
map.put("haha",432);
System.out.println(map);
定义一个List集合存储Computer类型数据,要求根据电脑的价格做降序排序
6.如何处理HashMap线程不安全问题 :
Hashtable代替HashMap
使用Collections工具类的static <K,V>Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
juc高级并发编程包ConcurrentHashMap<K,V> --> 推荐
7. Properties :
Properties类表示一组持久的属性
Properties可以保存到流中或从流中加载。
属性列表中的每个键及其对应的值都是一个字符串。
7.1 注意 :
应用场景 : 从properties格式的配置文件中读取键值对数据
步骤 :
1.定义xx.properties的配置文件
2.构建Properties类型的对象
3.调用load方法实现从指定流中加载
4.调用getProperty方法根据key获取value从文件中加载数据