泛型
概念
泛型可以约束集合中的元素类型.
泛型主要是想要模拟数组--自动检查集合中的元素类型 + 如果类型不对,直接编译期就报错
泛型 的标志是 < > ,Generics .
泛型是(Generics)是JDK1.5 的一个新特性,其实就是一个『语法糖』,本质上就是编译器为了提供更好的可读性而提供的一种小手段,小技巧,虚拟机层面是不存在所谓『泛型』的概念的。
举例:
import java.util.ArrayList;
import java.util.List;
public class Test_Generics {
public static void main(String[] args) {
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
if(classStringArrayList.equals(classIntegerArrayList)) {
System.out.println("类型相同");//类型相同
}
}
}
常用表示泛型的名称
--E - Element (在集合中使用,因为集合中存放的是元素)
--T - Type(Java 类)
--K - Key(键)
--V - Value(值)
--N - Number(数值类型)
--? - 表示不确定的java类型
泛型的使用
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
泛型类
泛型类也就是把泛型定义在类上,这样用户在使用类的时候才把类型给确定下来。
public class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public void T setKey(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
import java.util.ArrayList;
import java.util.List;
public class Test_Generics2 {
public static void main(String[] args) {
//传入的实参类型需与泛型的类型参数类型相同,即为Integer和String.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
Generic<String> genericString = new Generic<String>("字符串");
System.out.println(genericInteger.getKey());//123456
System.out.println(genericString.getKey());//字符串
//不传入泛型类型
Generic generic1 = new Generic("111111");
Generic generic2 = new Generic(222222);
Generic generic3 = new Generic(333.333);
Generic generic4 = new Generic(false);
System.out.println(generic1.getKey());//111111
System.out.println(generic2.getKey());//222222
System.out.println(generic3.getKey());//333.333
System.out.println(generic4.getKey());//false
}
}
泛型类在继承时,可以明确父类(泛型类)的参数类型,也可以不明确。子类泛型参数应该同时明确自己以及父类的泛型。
public class Test_Generics3 {
public static void main(String[] args) {
A1<Child,Parent> a1 = new A1();
a1.a = new Parent();
a1.b = new Child();
a1.a.parent = 1;
a1.b.child = 2;
System.out.println(a1.a.parent);
System.out.println(a1.b.child);
}
}
abstract class A<T> {
public T a;
}
class A1<U extends T, T> extends A<T> {
public U b;
}
class Parent {
public int parent;
}
class Child extends Parent {
public int child;
}
泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,虽然我们只创建了一个泛型接口Generator<T>,但是我们可以为T传入无数个实参形成无数种类型的接口。
public interface Inter<T> {
public abstract void show(T t);
}
public class InterImpl implements Inter<String> {
@Override
public void show(String s) {
System.out.println(s);
}
}
public class InterImpl implements Inter<Integer> {
@Override
public void show(String s) {
System.out.println(s);
}
}
泛型方法
有时候只关心某个方法,那么使用泛型时可以不定义泛型类,而是只定义一个泛型方法。
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
public <T> void show(T t) {
System.out.println(t);
}
泛型通配符
类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
泛型上下边界
接收A类或者A类的子类。< ? extends T >
public void processElements(List<? extends A> elements){
for(A a : elements){
System.out.println(a.getValue());
}
}
接收A类或者A类的父类。< ? super T >
public static void insertElements(List<? super A> list){
list.add(new A());
list.add(new B());
list.add(new C());
}
Collection接口
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为collection 是有序的,而另一些则是无序的。
常用方法
Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和 Queue集合。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
//测试 Collection接口的方法
public class Test_Collection {
public static void main(String[] args) {
//1,创建对象
//Collection c = new Collection();//报错,因为接口不能实例化
Collection<Integer> c = new ArrayList<>();
//2,调用方法
c.add(1); //向集合中添加元素
c.add(2);
c.add(3);
c.add(4);
System.out.println(c);
//c.clear();//清空集合
System.out.println( c.contains(3) );//判断是否包含
System.out.println( c.equals("1234") );//判断是否相等
System.out.println( c.hashCode() );//获取哈希码值
System.out.println( c.isEmpty() );//判断是否为空
System.out.println( c.remove(2) );//移除元素
System.out.println( c.size() );//获取集合的长度/元素的个数
Object[] objs = c.toArray() ;//把集合里的元素,存入Object数组中
System.out.println( Arrays.toString(objs) );
//集合间的操作
Collection<Integer> c2 = new ArrayList<>();
c2.add(1);
c2.add(2);
System.out.println( c.addAll(c2) );//把c2加到c里去
System.out.println(c);//[1, 3, 4, 1, 2]
System.out.println( c.containsAll(c2) );//判断c里是否有c2
//System.out.println( c.removeAll(c2) );//移除掉c和c2里都有的--取差集
//System.out.println(c);
System.out.println( c.retainAll(c2) );//取交集
//仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
System.out.println(c);
//迭代集合Iterator<E> iterator()
//--返回一个可以迭代集合的迭代器--Iterator接口
Iterator<Integer> it = c.iterator() ;
//hasNext()用来判断有没有下一个元素,有就返回true
while( it.hasNext() ) {
Integer i = it.next() ;//next() 获取元素
System.out.println(i);
}
}
}
List接口
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与 set 不同,列表通常允许重复的元素。列表本身允许 null 元素的话,通常它们允许多个null 元素。
特点
--List里的元素有序
--List里的元素都有索引
--List里的元素允许重复
--List里可以有null
方法
返回值类型 | 方法名 | 描述 |
---|---|---|
boolean | add(E e) | 将指定的元素追加到此列表的末尾(可选操作)。 |
void | add(int index, E element) | 将指定的元素插入此列表中的指定位置(可选操作)。 |
boolean | addAll(Collection c) | 按指定集合的迭代器(可选操作)返回的顺序将指定集合中的所有元素附加到此列表的末尾。 |
boolean | addAll(int index, Collection c) | 将指定集合中的所有元素插入到此列表中的指定位置(可选操作)。 |
void | clear() | 从此列表中删除所有元素(可选操作)。 |
boolean | contains(Object o) | 如果此列表包含指定的元素,则返回true。 |
E | get(int index) | 返回此列表中指定位置的元素。 |
int | hashCode() | 返回此列表的哈希码值。 |
int | indexOf(Object o) | 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。 |
boolean | isEmpty() | 如果此列表不包含元素,则返回 true 。 |
Iterator | iterator() | 以正确的顺序返回该列表中的元素的迭代器。 |
int | lastIndexOf(Object o) | 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。 |
E | remove(int index) | 删除该列表中指定位置的元素(可选操作)。 |
E | set(int index, E element) | 用指定的元素(可选操作)替换此列表中指定位置的元素。 |
int | size() | 返回此列表中的元素数。 |
import java.util.ArrayList;
import java.util.List;
//测试 List接口的方法
public class Test_List {
public static void main(String[] args) {
//1,创建List对象
//List list = new List();//List是接口,不能new
List<Double> list = new ArrayList<>();
list.add(1.1);
list.add(2.2);
list.add(3.3);
list.add(2.2);
list.add(2.2);
list.add(null);
list.add(null);
//List接口的特点:有序+可重复+null+索引
System.out.println(list);//[1.1, 2.2, 3.3, 2.2, 2.2, null, null]
//2,调用方法
//TODO 继承自父接口Collection的方法们
//List接口的特有方法
list.add(3, 9.9);//在指定的索引处,添加指定的数据
System.out.println(list);//[1.1, 2.2, 3.3, 9.9, 2.2, 2.2, null, null]
System.out.println( list.get(0) );//1.1---按照索引获取元素
System.out.println( list.indexOf(2.2) );//1---获取2.2第一次出现的索引值
System.out.println( list.lastIndexOf(2.2) );//5---获取2.2最后一次出现的索引值
System.out.println( list.remove(6) );//null---移除索引6对应的元素
System.out.println( list.set(4, 0.1) );//2.2---把索引4的值替换成0.1
System.out.println(list);//[1.1, 2.2, 3.3, 9.9, 0.1, 2.2, null]
List<Double> list2 = list.subList(1, 4) ;
System.out.println(list2);//[2.2, 3.3, 9.9]---截取子List集合,含头不含尾[1,4)
}
}
List接口的实现类之一:ArrayList
ArrayList是List接口的主要实现类,ArrayList是一个动态数组,允许任何符合规则的元素插入包括null。 它能快速随机访问存储的元素,支持随机访问, 查询速度快, 增删元素慢。
特点
--底层是数组结构
--有下标
--元素有序
--可以重复
创建对象
ArrayList()
List接口的实现类之二:LinkedList
LinkedList是List接口的另一个实现,除了可以根据索引访问集合元素外,LinkedList还实现了Deque接口。
LinkedList内部以链表的形式保存集合中的元素,所以随机访问集合中的元素性能较差,但在频繁的插入或删除元素时有较好的性能
特点
--底层是链表结构
--有下标
--元素有序
--可以重复
创建对象
LinkedList()
方法
返回值 | 方法名 | 描述 |
---|---|---|
void | addFirst(E e) | 将指定元素插入此列表的开头。 |
void | addLast(E e) | 将指定元素添加到此列表的结尾。 |
E | getFirst() | 返回此列表的第一个元素。 |
E | getLast() | 返回此列表的最后一个元素。 |
boolean | offer(E e) | 将指定元素添加到此列表的末尾(最后一个元素)。 |
boolean | offerFirst(E e) | 在此列表的开头插入指定的元素。 |
boolean | offerLast(E e) | 在此列表末尾插入指定的元素。 |
E | peek() | 获取但不移除此列表的头(第一个元素)。 |
E | peekFirst() | 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。 |
E | peekLast() | 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。 |
E | poll() | 获取并移除此列表的头(第一个元素) |
E | pollFirst() | 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。 |
E | pollLast() | 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。 |
E | pop() | 从此列表所表示的堆栈处弹出一个元素。 |
void | push(E e) | 将元素推入此列表所表示的堆栈。 |
E | removeFirst() | 移除并返回此列表的第一个元素。 |
E | removeLast() | 移除并返回此列表的最后一个元素。 |
import java.util.LinkedList;
//测试 LinkedList
public class Test_LinkedList {
public static void main(String[] args) {
//1,创建对象
LinkedList<Integer> list = new LinkedList<>();
//2,调用方法
//TODO --继承自Collection接口和List接口的方法们~~
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
//LinkedList的特有方法--针对首尾元素
list.addFirst(0);//添加首元素
list.addLast(9);//添加尾元素
System.out.println( list.getFirst() );//0---获取首元素
System.out.println( list.getLast() );//9---获取尾元素
System.out.println( list.removeFirst() );//0---移除首元素
System.out.println( list.removeLast() );//9---移除尾元素
list.offerFirst(100);//添加首元素
list.offerLast(200);//添加尾元素
System.out.println( list.peekFirst() );//100---获取首元素
System.out.println( list.peekLast() );//200---获取尾元素
System.out.println( list.pollFirst() );//100---移除首元素
System.out.println( list.pollLast() );//200---移除尾元素
}
}
List接口的实现类之三:Vector
Vector大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。
List总结
ArrayList和LinkedList的区别
都是线程不安全,相对线程安全的Vector,执行效率高。此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和Vector的区别
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。大多数清空下使用 ArrayList而不是Vector,因为同步完全可以由自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。
Set接口
Set接口也是Collection的子接口,set接口没有提供额外的方法。
Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则会添加操作失败。
Set集合判断两个对象是否相同是根据 equals() 方法,而不是使用 == 运算符。
特点
--不能重复
--没有下标
Set接口的实现类之一:HashSet
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
当你向HashSet集合中 加入数据时,先根据哈希函数将这些元素正确地分布在桶中。
对此 set 进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例桶的数量)的“容量”的和 成比例。
因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
特点
--底层是一个哈希表/散列表(桶的概念)
--元素不重复
--元素没有下标
--只能存一个null
Set接口的实现类之二:LinkedHashSet
LinkedHashSet是HashSet的子类。
LinkedHashSet也是根据元素的hashCode值来决定元素的存储位置。但它同时使用双向链表维护元素的次序,元素的顺序与添加顺序一致。
由于LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。
LinkedHashSet不允许集合元素重复。
Set接口的实现类之三:TreeSet
TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
TreeSet底层使用红黑树结构存储数据元素。
TreeSet两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。
自然排序:
TreeSet会调用集合元素的compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列。
如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口。
实现Comparable的类必须实现compareTo(Object obj) 方法,两个对象即通过compareTo(Object obj) 方法的返回值来比 较大小。
Comparable的一些典型实现类:
-- BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较
--Character:按字符的 unicode值来进行比较
--Boolean:true 对应的包装类实例大于 false 对应的包装类实例
--String:按字符串中字符的 unicode 值进行比较
--Date、Time:后边的时间、日期比前面的时间、日期大
因为只有相同类的两个实例才会比较大小,所以向TreeSet中添加的应该是同一个类的对象。
对于TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj) 方法比较返回值。
当需要把一个对象放入TreeSet 中,重写该对象对应的equals() 方法时,应保证该方法与compareTo(Object obj) 方法有一致的结果。
对于TreeSet集合而言,它判断两个对象是否相等的标准是:两个对象通过compareTo(Object obj)方法比较是否返回0,如果返回0则相等。
定制排序
TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素,希望按照其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparator接口来实现。需要重写compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。
使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。
Map接口
Map与Collection并列存在。用于保存具有映射关系的数据:key-value
Map中的key和value都可以是任何引用类型的数据
Map中的key用Set来存放,不允许重复,即同一个Map对象所对应的类,须重写hashCode()和equals()方法
方法
返回值 | 方法名 | 描述 |
---|---|---|
void | clear() | 从此映射中移除所有映射关系(可选操作)。 |
boolean | containsKey(Object key) | 如果此映射包含指定键的映射关系,则返回 true。 |
boolean | containsValue(Object value) | 如果此映射将一个或多个键映射到指定值,则返回 true。 |
Set<Map.Entry<K,V>> | entrySet() | 返回此映射中包含的映射关系的 Set 视图。 |
boolean | equals(Object o) | 比较指定的对象与此映射是否相等。 |
V | get(Object key) | 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 |
int | hashCode() | 返回此映射的哈希码值。 |
boolean | isEmpty() | 如果此映射未包含键-值映射关系,则返回 true。 |
Set<K> | keySet() | 返回此映射中包含的键的 Set 视图。 |
V | put(K key, V value) | 将指定的值与此映射中的指定键关联(可选操作)。 |
void | putAll(Map<? extends K,? extends V> m) | 从指定映射中将所有映射关系复制到此映射中(可选操作)。 |
V | remove(Object key) | 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 |
int | size() | 返回此映射中的键-值映射关系数。 |
Collection<V> | values() | 返回此映射中包含的值的 Collection 视图。 |
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
//测试 Map接口
public class Test_Map {
public static void main(String[] args) {
//1,创建对象
//Map<Integer,String>--约束Map集合里的key的类型是Integer,value的类型是String
Map<Integer,String> map = new HashMap<>();
//2,调用方法
map.put(9527, "唐伯虎") ;
map.put(9530, "石榴姐") ;
map.put(9528, "如花") ;
map.put(9529, "秋香") ;
//特点: 无序 + key重复的话,value会被覆盖
map.put(9531, "如花") ;
map.put(9528, "如花") ;
System.out.println( map.containsKey(9000) );//false---判断是否包含指定的key
System.out.println( map.containsValue("唐伯虎") );//true---判断是否包含指定的value
System.out.println( map.equals("唐伯虎") );//false---判断是否相等
System.out.println( map.get(9530) );//石榴姐---根据key获取value
System.out.println( map.hashCode() );//54462703---获取哈希码值
System.out.println( map.isEmpty() );//false---判断是否为空
System.out.println( map.remove(9528) );//如花---根据key删除记录
System.out.println( map.size() );//4---获取map的长度/元素个数
Map<Integer,String> map2 = new HashMap<>();
//2,调用方法
map2.put(1000, "钢铁侠") ;
map2.put(2000, "灭霸") ;
map.putAll(map2); //把map2加到map里
//迭代map集合方式1 -- 键集
Set<Integer> set = map.keySet() ;
//遍历set集合方式1 : 得到每个key
Iterator<Integer> it = set.iterator() ;
while(it.hasNext()) {//判断有没有数据
Integer key = it.next() ;
//拿着key回map里找value
String value = map.get(key) ;
System.out.println(key+":::::::::"+value);
//2000:::::::::灭霸
//9527:::::::::唐伯虎
//1000:::::::::钢铁侠
//9529:::::::::秋香
//9530:::::::::石榴姐
//9531:::::::::如花
}
//遍历set集合方式2 : foreach--数组|Collection集合
for(Integer key : set) {
//拿着key回map里找value
String value = map.get(key) ;
System.out.println(key+"------"+value);
//2000------灭霸
//9527------唐伯虎
//1000------钢铁侠
//9529------秋香
//9530------石榴姐
//9531------如花
}
//迭代map集合方式2 -- 值集
//Collection<V> values() --把map里的value们存入Collection
Collection<String> c = map.values() ;
System.out.println(c);//[灭霸, 唐伯虎, 钢铁侠, 秋香, 石榴姐]
//迭代map集合方式3 -- 键-值映射集
//Set<Map.Entry<K,V>> entrySet() --把map的key和value同时封装成Entry对象存入set
Set< Entry<Integer,String> > set2 = map.entrySet() ;
//遍历set得到每个Entry对象
for(Entry<Integer,String> entry : set2) {
Integer key = entry.getKey() ;//获取key
String value = entry.getValue() ;//获取value
System.out.println(key+"======="+value);
//2000=======灭霸
//9527=======唐伯虎
//1000=======钢铁侠
//9529=======秋香
//9530=======石榴姐
//9531=======如花
}
}
}
Map接口的实现类之一:HashMap
HashMap是Map接口使用频率最高的实现类。允许使用null键和null值,与HashSet一样,不保证映射的顺序。
所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()和hashCode()
所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()
HashMap判断两个key相等的标准是:两个key通过equals() 方法返回true, hashCode值也相等。
HashMap判断两个value相等的标准是:两个value通过equals()方法返回true。
HashMap可以使用null值为key或value
HashMap的存储结构
JDK8之前版本:
JDK8之前HashMap的存储结构是数组+链表结构(即为链地址法) ,当实例化一个HashMap时,系统会创建一个长度为Capacity的Entry数组,这个长度在哈希表中被称为容量 (Capacity),在这个数组中可以存放元素的位置我们称之“桶”(bucket),每个bucket都有自己的索引,系统可以根据索引快速的查找bucket中的元素。
每个bucket中存储一个元素,即一个Entry对象,但每一个Entry对象可以带一个引用变量,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Entry链。 而且新添加的元素作为链表的head。
JDK8之后版本:
JDK8之后HashMap的存储结构是数组+链表+红黑树实现。当实例化一个HashMap时,会初始化initialCapacity和loadFactor,在put第一对映射关系时,系统会创建一个长度为initialCapacity的Node数组,这个长度在哈希表中被称为容量(Capacity),在这个数组中可以存放元素的位置我们称之为 “桶”(bucket),每个bucket都有自己的索引,系统可以根据索引快速的查找bucket中的元素。
每个bucket中存储一个元素,即一个Node对象,但每一个Node对象可以带一个引用变量next,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Node链。也可能是一个一个TreeNode对象,每一个TreeNode对象可以有两个叶子结点left和right,因此,在一个桶中,就有可能生成一个TreeNode树。而新添加的元素作为链表的last,或树的叶子结点。
Map接口的实现类之二:LinkedHashMap
LinkedHashMap 是 HashMap的子类,在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序。 该链表负责维护Map的迭代顺序,与插入顺序一致,因此性能比HashMap低,但在迭代访问Map里的全部元素时有较好的性能。
Map接口的实现类之三:Hashtable
HashMap线程不安全,Hashtable是线程安全的。
HashMap可以使用null值为key或value,Hashtable不允许使用null作为key和value。
Hashtable实现原理和HashMap相同,底层都使用哈希表结构,查询速度快。
Hashtable和HashMap一样也不能保证其中Key-Value对的顺序
Map接口的实现类之四:Properties
Properties类是Hashtable 的子类,该对象用于处理属性文件。
由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型 。
Map接口的实现类之五:TreeMap
TreeMap存储Key-Value对时,需要根据key-value对进行排序。TreeMap可以保证所有的Key-Value对处于有序状态。底层采用红黑树的数据结构。
TreeMap也有两种排序方式,自然排序和定制排序。
Collections工具类
Collections是一个操作 Set、List 和 Map 等集合的工具类。
Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。
方法
返回值 | 方法名 | 描述 |
---|---|---|
static <T> boolean | addAll(Collection<? super T> c, T... elements) | 将所有指定元素添加到指定 collection 中。 |
min(Collection<? extends T> coll) | 根据元素的自然顺序 返回给定 collection 的最小元素。 | |
max(Collection<? extends T> coll | 根据元素的自然顺序,返回给定 collection 的最大元素。 | |
static void | reverse(List<?> list) | 反转指定列表中元素的顺序。 |
static <T> boolean | replaceAll(List<T> list, T oldVal, T newVal) | 使用另一个值替换列表中出现的所有某一指定值。 |
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//测试 Collections工具类--为集合提供的各种方法
public class Test_Collections {
public static void main(String[] args) {
// 创建List集合
List<Integer> list = new ArrayList<>();
// 添加元素
list.add(9);
list.add(1);
list.add(6);
list.add(3);
list.add(7);
System.out.println(list);//[9, 1, 6, 3, 7]
list.clear();//清空
// Collections优化
Collections.addAll(list, 9, 1, 6, 3, 7);
System.out.println(list);// [9, 1, 6, 3, 7]
// max()--获取集合里的最大值
System.out.println(Collections.max(list));//9
// min()--获取集合里的最小值
System.out.println(Collections.min(list));//1
// reverse()--翻转list集合
Collections.reverse(list);
System.out.println(list);// [7, 3, 6, 1, 9]
// replaceAll()--把旧值替换成新值
Collections.replaceAll(list, 9, 8);
System.out.println(list);// [7, 3, 6, 1, 8]
}
}
Iterator迭代器接口
Iterator接口用来遍历集合元素
Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了 Iterator接口的对象。
集合对象每次调用iterator()方法都得到一个全新的迭代器对象
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
//测试 List接口的方法
public class Test_Iterator {
public static void main(String[] args) {
// 1,创建List对象
List<Double> list = new ArrayList<>();
list.add(1.1);
list.add(2.2);
list.add(3.3);
list.add(2.2);
list.add(2.2);
list.add(null);
list.add(null);
// [1.1, 2.2, 3.3, 2.2, 2.2, null, null]
System.out.println(list);
// 迭代List集合方式1: Iterator<E> iterator()--继承自父接口Collection的,返回父接口
Iterator<Double> it = list.iterator();
while (it.hasNext()) {
Double next = it.next();
System.out.println(next);
}
// 迭代List集合方式2: ListIterator<E> listIterator()--List子接口特有的返回子接口--可以向后遍历也提供了逆向遍历
ListIterator<Double> it2 = list.listIterator();
// 顺序向后遍历
while (it2.hasNext()) {// 判断有没有下一个元素
Double next = it2.next();// 获取下一个元素
}
// 逆向遍历--必须先顺序遍历完才能逆向!
while (it2.hasPrevious()) {// 判断有没有前一个元素
Double next = it2.previous();// 获取前一个元素
System.out.println(next);
}
// 迭代List集合方式3: 因为List集合有下标,所以可以用下标遍历
for (int i = 0; i < list.size(); i++) {// list.size()集合的长度
Double next = list.get(i);// get()根据下标i获取元素
System.out.println(next);
}
// 迭代List集合方式4: foreach / 增强for循环 -- 只能循环 -- 数组|Collection集合
// 语法:for(遍历得到的数据的类型 变量名 : 想要遍历的容器){ 循环体 }
for (Double d : list) {
System.out.println(d); // 打印获取到的数据
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。