泛型的介绍
泛型:
一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的类型。参数化类型,把类型当作参数一样传递
格式:
<数据类型>
注意:此处的数据类型只能是引用类型
泛型的好处
把运行时期的问题提前到了编译时期
避免了强制类型转换
优化程序,消除黄色警告线
泛型的举例应用
泛型存储String类型数据举例
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
// list.add(100);直接就报错了
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String r = iterator.next();
System.out.println(r);
}
}
}
//hello
//world
泛型类
泛型类:把泛型定义在类上
public class GenericTool<T> {
private T obj;
public T getObj(){
return obj;
}
public void setObj(T obj){
this.obj = obj;
}
}
public static void main(String[] args) {
GenericTool genericTool = new GenericTool();
genericTool.setObj(new String("zhangsan"));
Object obj = genericTool.getObj();
String obj1 = (String) obj;
System.out.println(obj1);
genericTool.setObj(new String("lisi"));
Object obj2 = genericTool.getObj();
String obj21 = (String) obj2;
System.out.println(obj21);
GenericTool<String> stringGenericTool = new GenericTool<String>();
stringGenericTool.setObj("wangwu");
String obj3 = stringGenericTool.getObj();
System.out.println(obj3);
}
}
//zhangsan
//lisi
//wangwu
Set集合
Set接口
Set接口的引入
Collection接口下有List和Set接口
List 元素有序(存储顺序和取出顺序一致,可以重复)
Set 无序(存储顺序和取出顺序不一致,元素唯一)
HashSet:底层是hash表,线程不安全,效率高,有时候,给的顺序正好好存储的顺序一致,但这并不代表有序,可以多试试看,元素唯一,无序
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
//创建字符串对象并添加到集合中
hashSet.add("hello");
hashSet.add("world");
hashSet.add("java");
hashSet.add("hello");
hashSet.add("world");
for(String s:hashSet){
System.out.println(s);
}
}
}
//world
//java
//hello
HashSet类
HashSet类概述
不保证set的迭代顺序
特别是不保证该顺序恒久不变
HashSet如何保证元素的唯一性
底层数据结构是哈希表(元素是链表的数组)
哈希表依赖于哈希值存储
添加功能底层依赖的两个方法
int hashCode()
boolean equals(Object obj)
public static void main(String[] args) {
HashSet中add方法的源码
public interface Collection<E> extends Iterable<E>{
...
}
public interface Set<E> extends Collection<E>{
...
}
public class HashSet<E> implements Set<E>{
//由哈希表(实际为HashMap实例)支持
private transient HashMap<E,Object> map;
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
static final int hash(Object key) { //key -- e -- 要插入的元素
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
public V put(K key, V value) { //key -- e -- 要插入的元素
return putVal(hash(key), key, value, false, true);
}
//HashSet中的add()方法,最终我们发现调用的是HashMap中的putVal()方法
//hash的值与元素的HashCode()方法有关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
//哈希表存储的是一个个的节点数组
Node<K,V>[] tab; Node<K,V> p; int n, i;
//判断哈希表是否初始化,如果没有初始化就进行初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//根据元素对象的哈希值计算出存储位置,如果该元素的位置是null就代表,该位置没有
//元素,就创建新的节点,就存储元素
//通过源码我们发现,HashSet的add()方法一定和HashCode()方法有关系
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
/*
存入的元素和以前的元素的哈希值进行比较
如果哈希值不同,继续向下执行,把元素添加到集合
如果哈希值一样,会调用对象的equals()方法进行比较
如果返回的是false,会继续向下执行,然后将元素添加到集合中
如果返回的是true,说明元素内容一样,重复了,就不存储
*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
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);
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;
}
}
LinkedHashSet
LinkedHashSet:底层由哈希表和链表组成
哈希表保证元素的唯一性
链表保证元素的顺序,有序,(存储和取出的顺序一致)
public static void main(String[] args) {
LinkedHashSet<String> strings = new LinkedHashSet<String>();
strings.add("hello");
strings.add("world");
strings.add("java");
for(String s:strings){
System.out.println(s);
}
}
}
//hello
//world
//java