泛型
1.泛型简介:
1.1泛型的基本概念:
1.2泛型的好处:
代码可读性更好(不用强制转换);程序更加安全
1.3类型擦除:
编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”
2.泛型的使用:
2.1定义泛型:
泛型字符可以是任何标识符,一般采用E,T,K,V,N,?
2.2泛型类:
泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来。泛型类的具体使用方法是在类的名称后添加一个或多个类型参数声明,如、<T,K,V>
2.2.1语法结构
public class ***<泛型表示符号>{
}
2.2.2示例
public class Generic <T>{
private T flag;
public void setFlag(T flag) {
this.flag = flag;
}
public T getFlag() {
return this.flag;
}
}
public class Test {
public static void main(String[] args) {
Generic<String> generic = new Generic<>();
generic.setFlag("admin");
String flag = generic.getFlag();
System.out.println(flag);
}
}
2.3泛型接口:
泛型接口和泛型类的声明方式一致。泛型接口的具体类型需要在实现类中进行声明。
2.3.1语法结构:
public interface ***<泛型表示符号>{
}
2.3.2示例
//泛型接口
public interface Igeneric<T> {
T getName(T name);
}
//接口实现类
public class IgenericImpl implements Igeneric<String>{
@Override
public String getName(String name) {
// TODO Auto-generated method stub
return name;
}
}
//测试类
public class Test2 {
public static void main(String[] args) {
IgenericImpl igeneric = new IgenericImpl();
String name = igeneric.getName("JioH");
System.out.println(name);
Igeneric<String> igeneric2 = new IgenericImpl();
String name2 = igeneric2.getName("JioH");
System.out.println(name2);
}
}
2.4泛型方法:
2.4.1非静态方法
2.4.1.1语法结构
public <泛型表示符号> void getName(泛型表示符号 name){
}
public <泛型表示符号> 泛型表示符号 getName(泛型表示符号 name){
}
2.4.1.2示例:
public class MethodGeneric {
//无返回值
public<T> void setName(T name) {
System.out.println(name);
}
//有返回值
public<T> T getName(T name) {
return name;
}
}
public class Test3 {
public static void main(String[] args) {
MethodGeneric mg = new MethodGeneric();
mg.setName("JioH");
mg.setName(951933);
MethodGeneric mg1 = new MethodGeneric();
String name = mg1.getName("JioH");
System.out.println(name);
Integer name1 = mg1.getName(951933);
System.out.println(name1);
}
}
2.4.2静态方法:
静态方法无法访问类上的泛型
只能在方法上定义泛型
2.4.2.1语法结构:
public static<泛型表示符号> void getName(泛型表示符号 name){
}
public static<泛型表示符号> 泛型表示符号 getName(泛型表示符号 name){
}
2.4.2.2示例:
public class MethodGeneric {
//无返回值
public static <T> void getFlag(T flag) {
System.out.println(flag);
}
//有返回值
public static <T> T setFlag(T flag) {
return flag;
}
}
public class Test4{
public static void main(String[] args) {
//静态方法不需要实例化,用过类名直接调用
MethodGeneric.setFlag("JioH");
MethodGeneric.setFlag(123123);
String flag = MethodGeneric.getFlag("jioh");
Integer flag1 = MethodGeneric.getFlag(123123);
System.out.println(flag);
System.out.println(flag1);
}
}
2.4.3泛型方法与可变参数
在泛型方法中,泛型也可以定义可变参数类型
2.4.3.1语法结构
public <泛型表示符号> void showMsg(泛型表示符号... args){
}
2.4.3.2示例
public <T> void method(T...args) {
//遍历数组
for(T t:args) {
System.out.println(t);
}
}
public class Test5{
public static void main(String[] args) {
//实例化
MethodGeneric mg = new MethodGeneric();
String[] arr = new String[] {"a","b","c"};
mg.method(arr);
Integer[] arr2 = new Integer[] {1,2,3};
mg.method(arr2);
}
}
2.5通配符与上下限定:
只能在<>中使用
2.5.1无界通配符
2.5.1.1语法结构:
public void showFlag(Generic<?> generic){
}
2.5.1.2示例:
public class Generic <T>{
private T flag;
public void setFlag(T flag) {
this.flag = flag;
}
public T getFlag() {
return this.flag;
}
}
public class ShowMsg {
public void showFlag(Generic<?> generic){
System.out.println(generic.getFlag());
}
}
public class Test6 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(20);
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50);
showMsg.showFlag(generic1);
Generic<String> generic2 = new Generic<>();
generic2.setFlag("JioH");
showMsg.showFlag(generic2);
}
}
2.5.2通配符的上限限定
上限限定表示通配符的类型是T类以及T类的子类或者T接口以及T接口的子接口;该方式同样适用于与泛型的上限限定
2.5.2.1语法结构
public void showFlag(Generic<? extends Number> generic){
}
2.5.2.2示例:
public class ShowMsg {
public void showFlag(Generic<? extends Number> generic){
System.out.println(generic.getFlag());
}
}
2.5.3通配符的下限限定
下限限定表示通配符的类型是T类以及T类的父类或者T接口以及T接口的父接口;该方式不适用于泛型类
2.5.3.1语法结构
public void showFlag(Generic<? super Number> generic){
}
2.5.3.2示例:
public class ShowMsg {
public void showFlag(Generic<? super Integer> generic){
System.out.println(generic.getFlag());
}
}
3泛型总结
1.基本类型不能用于泛型 Test t
2.不能通过类型参数创建对象 T elm = new T();
容器
1.容器的结构
1.1结构图
1.1.1单例集合
将数据一个一个的存储
1.1.2双例集合
基于key和value的结构存储数据
2.单例集合的使用
2.1 Collection接口介绍
Collection是单例集合的根接口,它的两个子接口是List、Set接口
2.2 Collection接口中的抽象方法
2.3 List接口
2.3.1 List接口特点
有序、可重复
2.3.2 List的常用方法
2.4 ArrayList容器类
ArrayList是List接口的实现类。是List存储特征的具体实现。
ArrayList底层是用数组实现的存储。特点:查询效率高、增删效率低,线程不安全
2.4.1添加元素
public class ArrayListTest {
public static void main(String[] args) {
//实例化 ArrayList容器
List<String> list = new ArrayList<>();
//添加元素
boolean flag = list.add("123");
System.out.println(flag);
list.add(1, "JioH");
}
}
2.4.2获取元素
get
for(int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
2.4.3替换元素
set
String val = list.set(0, "JioH");
System.out.println(val);
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
//转换为object[]
//不能在转化的方法调用时进行强制类型转换
Object[] arr = list.toArray();
for(int i=0;i<arr.length;i++){
String str = (String)arr[i];
System.out.println(str);
}
//将单例集合转换为指定类型的数组
//类型需要参考泛型中的类型
String[] arr2 = list.toArray(new String[list.size()]);
for(int i=0;i<arr2.length;i++){
System.out.println(arr2[i]);
}
2.4.4容器的并集操作
集合不可为空,若为空会返回FALSE
List<String> a = new ArrayList<>();
a.add("a");
a.add("b");
a.add("c");
List<String> b = new ArrayList<>();
b.add("d");
b.add("e");
//a并b
boolean flag5 = a.addAll(b);
System.out.println(flag5);
for(String str:a) {
System.out.println(str);
}
2.4.5容器的交集操作
boolean flag6 = a.retainAll(b);
2.4.6容器的差集操作
//去掉a集合中,b有的元素
boolean flag6 = a.removeAll(b);
2.4.7源码分析
2.4.7.1 ArrayList底层存储方式
ArrayList底层是用数组实现的存储
2.5 Vector容器类
Vector底层是用数组实现的,线程安全低效
2.5.1 Vector的使用
Vector的使用与ArrayList是相同的,都实现了List接口,对List接口中的抽象方法做了具体实现
public class VectorTest {
public static void main(String[] args) {
//实例化Vector
List<String> v = new Vector<>();
v.add("a");
v.add("b");
v.add("a");
for(int i=0;i<v.size();i++) {
System.out.println(v.get(i));
}
//迭代
for(String str:v) {
System.out.println(str);
}
}
}
2.5.2 Stack容器
Stack栈容器,是Vector的一个子类,它实现了一个标准的后进先出的栈。
2.5.2.1操作栈的方法
2.5.2.2 Stack的使用
public class StackTest {
public static void main(String[] args) {
//实例化栈容器
Stack<String> s = new Stack<>();
//将元素添加到栈容器中
s.push("a");
s.push("b");
s.push("c");
System.out.println(s.empty()); //false
//查看栈顶元素
System.out.println(s.peek());
//返回元素在栈容器中的位置
System.out.println(s.search("c"));
//返回1 从栈顶开始计数
//从容器中取出元素
String s1 = s.pop();
System.out.println(s1);
String s2 = s.pop();
System.out.println(s2);
String s3 = s.pop();
System.out.println(s3);
System.out.println(s.empty()); //true
}
}
2.5.2.3 Stack的使用案例
//匹配符号的对称性
public class StackTest {
public static void main(String[] args) {
StackTest stacktest = new StackTest();
stacktest.symmetry();
}
public void symmetry() {
String str = "...{...[...(...)...]...}..(...)..[..]...";
//实例化
Stack<String> stack = new Stack<>();
boolean flag = true;
for(int i = 0;i<str.length();i++) {
char c = str.charAt(i);
if(c == '{') {
stack.push("}");
}
if(c == '[') {
stack.push("]");
}
if(c == '(') {
stack.push(")");
}
//判断符号是否匹配
if(c == '}' || c == ']' || c == ')') {
if(stack.empty()) {
flag = false;
break;
}
String x = stack.pop();
//单个字符串转字符
if(x.charAt(0) != c) {
flag = false;
break;
}
}
}
if(!stack.empty()) {
flag = false;
}
System.out.println(flag);
}
2.6 LinkedList 容器类
LinkedList底层用双向链表实现的存储。特点是:查询效率低、增删效率高、线程不安全。
LinkedList实现了List接口,所以LinkedList是具备List的存储特征的(有序,元素可重复)。
2.6.1 LinkedList的使用
public class LinkedListTest {
public static void main(String[] args) {
//实例化
List<String> list = new LinkedList<>();
//添加元素
list.add("a");
list.add("b");
list.add("c");
list.add("a");
//获取元素
for(int i = 0;i<list.size();i++) {
System.out.println(list.get(i));
}
}
}
2.6.2 LinkedList的使用(非List标准)
public class LinkedListTest {
public static void main(String[] args) {
//实例化
LinkedList<String> linkedlist = new LinkedList<>();
//在第一个位置添加一个元素
linkedlist.addFirst("xixi");
linkedlist.addFirst("haha");
linkedlist.addFirst("xixihaha");
for(String str:linkedlist) {
System.out.println(str);
}
//在最后一个位置添加元素
linkedlist.addLast("无变");
linkedlist.addLast("嘎乐");
linkedlist.addLast("嘻嘻哈哈");
//获取第一个元素
System.out.println(linkedlist.getFirst());
//获取最后一个元素
System.out.println(linkedlist.getLast());
//移除第一个元素
linkedlist.removeFirst();
//移除最后一个元素
linkedlist.removeLast();
//从此列表所表示的堆栈中弹出一个元素,等效于removeFirst
linkedlist.pop();
//将元素推入此列表所表示的堆栈中,等效于addFirst
linkedlist.push("哈哈哈哈");
boolean flag = linkedlist.isEmpty();
System.out.println(flag);
}
}
2.6.3 LinkedList源码分析
2.6.3.1 节点类
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
//上一个节点的地址 元素对象的地址 下一个节点的地址
2.6.3.2 成员变量
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
2.6.3.3 添加元素
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
2.6.3.4 头尾添加元素
2.6.3.4.1 addFirst
/**
* Inserts the specified element at the beginning of this list.
*
* @param e the element to add
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* Links e as first element.
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
2.6.3.4.2 addLast
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #add}.
*
* @param e the element to add
*/
public void addLast(E e) {
linkLast(e);
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
2.6.3.5 在指定位置添加元素
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
2.6.3.6 获取元素
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* Tells if the argument is the index of an existing element.
*/
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
2.7 Set接口
Set继承自Collection,方法与Collection保持一致。
2.7.1 特点
无序、不可重复。
常用实现类:HashSet、TreeSet等;一般使用HashSet
2.7.2 HashSet
HashSet是一个没有重复元素的集合,不保证元素的顺序。而且HashSet允许有null元素。HashSet是采用哈希算法实现,底层实际是用HashMap实现的(HashSet本质是一个简化版的HashMap),因此,查询效率和增删效率都比较高。
2.7.2.1 Hash算法原理
Hash算法也称之为散列算法。
2.7.3 HashSet的使用
public class HashSetTest {
public static void main(String[] args) {
//实例化HashSet
Set<String> set = new HashSet<>();
//添加元素
set.add("a");
set.add("b");
set.add("c");
//获取元素,在Set容器中没有索引,所以没有get(int index)方法
for(String str:set) {
System.out.println(str);
}
//删除元素
set.remove("a");
//返回元素个数
int size = set.size();
System.out.println(size);
}
}
2.7.4 HashSet存储特征分析
不保证元素顺序,没有重复元素,线程不安全。允许有null元素。
无序:
在HashSet中底层是用HashMap存储元素的,HashMap底层使用的是数组与链表实现元素的存储。元素在数组中存放时,并不是有序存放的,也不是随机存放的,而是对元素的哈希值(通过散列算法得出)进行元素决定元素在数组中的位置。
不重复:
当两个元素的哈希值进行运算后得到相同的在数组中的位置时,会调用元素的equals()方法判断两个元素是否相同。如果元素相同则不会添加该元素,如果不相同则会使用单向链表保存该元素。
2.7.5 通过HashSet存储自定义对象
2.7.5.1 创建Users对象
public class Users {
private String username;
private int userage;
public Users(String username,int userage) {
this.username = username;
this.userage = userage;
}
public Users() {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + userage;
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Users other = (Users) obj;
if (userage != other.userage)
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
@Override
public String toString() {
return "Users [username=" + username + ", userage=" + userage + "]";
}
}
2.7.5.2 在HashSet中存储Users对象
public class HashSetTest {
public static void main(String[] args) {
//实例化HashSet
Set<Users> set = new HashSet<>();
Users u1 = new Users("JioH",18);
Users u2 = new Users("JioH",18);
set.add(u1);
set.add(u2);
for(Users users:set) {
System.out.println(users);
}
}
}
2.7.6 TreeSet容器类
TreeSet是一个可以对元素进行排序的容器。底层实际是使用TreeMap实现的,内部维持了一个简化版的TreeMap,通过key来存储Set的元素。TreeSet内部需要对存储的元素进行排序,因此,我们需要给定排序规则
排序规则实现方式:
通过元素自身实现比较规则。
通过比较器指定比较规则。
2.7.6.1 TreeSet的使用
public class TreeSetTest {
public static void main(String[] args) {
//实例化
Set<String> set = new TreeSet<>();
//添加元素
set.add("a");
set.add("d");
set.add("b");
set.add("a");
for(String str:set) {
System.out.println(str);
}
}
}
2.7.7 通过元素自身实现比较规则
2.7.7.1 创建Users类
public class Users implements Comparable<Users>{
private String username;
private int userage;
public Users(String username,int userage) {
this.username = username;
this.userage = userage;
}
public Users() {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + userage;
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Users other = (Users) obj;
if (userage != other.userage)
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
@Override
public String toString() {
return "Users [username=" + username + ", userage=" + userage + "]";
}
//定义比较规则
@Override
public int compareTo(Users o) {
if(this.userage < o.getUserage()) {
return 1;
}
if(this.userage == o.getUserage()) {
return this.username.compareTo(o.getUsername()); //比较名字
}
// TODO Auto-generated method stub
return -1;
}
}
2.7.7.2 在TreeSet中存放Users对象
public class TreeSetTest {
public static void main(String[] args) {
//实例化
Set<Users> set = new TreeSet<>();
Users u = new Users("JioH",18);
Users u1 = new Users("xixihaha",20);
Users u2 = new Users("axixihaha",20);
set.add(u);
set.add(u1);
set.add(u2);
for(Users Users:set) {
System.out.println(Users);
}
}
}
2.7.8 通过比较器实现比较规则
通过比较器定义比较规则时,我们需要单独创建一个比较器,比较器需要实现Comparator接口中的compare方法来定义比较规则。在实例化TreeSet时将比较器对象交给TreeSet来完成元素的排序处理。此时元素自身就不需要实现比较规则了。
2.7.8.1 创建比较器
public class StudentComparator implements Comparator<Student>{
//定义比较规则
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
if(o1.getAge() > o2.getAge()) {
return 1;
}
if(o1.getAge() == o2.getAge()) {
return o1.getName().compareTo(o2.getName());
}
return -1;
}
}
2.7.8.2 创建Student对象
public class Student {
private String name;
private int age;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
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 String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
2.7.8.3 在TreeSet中存储Users对象
public class TreeSetTest {
public static void main(String[] args) {
//实例化
Set<Student> set = new TreeSet<>(new StudentComparator());
Student s = new Student("JIOH",18);
Student s1 = new Student("jioh",16);
Student s2 = new Student("JioH",22);
set.add(s);
set.add(s1);
set.add(s2);
for(Student str:set) {
System.out.println(str);
}
}
}