泛型: 是一种把类型明确的工作推迟到对象创建或者调用方法时才能确定的类型.
参数化类型,把类型当作参数一样的传递;
泛型可以定义多个,泛型名间使用逗号隔开.
<数据类型> 数据类型只能是应用数据类型.
# A:把运行时的数据类型转换异常问题提前到了编译期;
# B:避免数据的强制类型转换.
# 泛型类:创建对象时确定数据类型.
* 格式:public class 类名<泛型名,...>{}
# 泛型方法:根据传入的参数类型确定泛型的具体数据类型.
* 格式:publiv <泛型名> void 方法名(泛型名 变量名){}
# 泛型接口:
* 格式:public interface 接口名<泛型名,...> {}
# 泛型接口中的泛型的数据类型确定:
* A:子类在实现该接口确定泛型的数据类型
* public class 类名 implements 泛型接口名<泛型的具体数据类型> {}
* B:子类在实现该接口的不确定该泛型的数据类型,由创建该类对象时确定,则该类名后必须声明泛型
* public class 类名<泛型名> implements 泛型接口名<泛型名> {}
* ?:代表任意类型,如果没有明确,可以是Object类和任意类型的Java类.
* ? extends E:向下限定,E及其子类.
* ? super E:向上限定,E及其父类.
public class MyGenericDemo {
public static void main(String[] args) {
// 创建泛型类对象,根据设定的数据类型不同,name的数据类型相应变化
MyShow ms = new MyShow();
ms.setName("somnus");
System.out.println(ms.getName());
MyShow ms2 = new MyShow();
ms2.setName(12);
System.out.println(ms2.getName());
// 调用泛型方法
Show.show("somnus");
Show.show(123);
// 泛型接口
// 情况1
MyInterClass mic = new MyInterClass();
mic.show("somnus");
// 情况2,和泛型类的使用方式相同
MyInterClass2 mic2 = new MyInterClass2();
mic2.show(89);
// 泛型通配符
// ?:代表任意类型,如果没有明确,可以是Object类和任意类型的Java类.
List> list1 = new ArrayList
Collection集合是单列集合的顶层接口,其有两个子接口,List接口和Set接口.
#长度区别:数组长度固定,集合长度可变;
#存储内容区别:数组储存同种数据类型元素,集合可以存储不同数据类型元素;
#储存的数据类型的区别:数组可以存储基本数据类型和引用数据类型,集合只能存储引用数据类型.
boolean add(Object obj):添加一个元素到集合中;
boolean addAll(Collection c):添加一个集合的元素到该集合中;
void clear():删除所有的元素;
boolean remove(Object o):删除指定元素;
boolean removeAll(Collection c):移除此集合中和指定集合中的所有相同元素,该集合发生变化;
boolean contains(Object o):判断集合是否包含指定元素;
boolean containsAll(Collection c):判断集合中是否包含指定集合的元素(所有元素);
boolean isEmpty():判断集合是否为空(没有数据);
Iterator iterator():返回在此collection的元素上进行迭代的迭代器;
int size():集合的长度;
boolean retainAll(Collection c):保留此集合中和指定集合中相同的元素,将结果保存在该集合中,该集合发生变化返回true;
Object[] toArray():将集合转换为数组;
# 迭代器Iterator:集合专用遍历方式(依赖集合存在),其实现是在集合具体的类中以私有的内部类实现的.
# boolean hasNext():判断是否还有元素,如果不使用next获取,则该方法指向的元素不变(指针不变).
# next():获取下一个元素(指针移动).
用来替代集合循环的迭代器.
好处:简化了数组和集合的遍历.
for(元素数据类型 变量名:数组或Collection集合) {
使用变量,该变量就是元素
}
* A:使用增强for循环的不能为空(使用之前进行判断是否为空);
* B:存在并发修改异常的情况;
* C:没有索引值,不能准确的定位元素.
public class MyForEachDemo {
public static void main(String[] args) {
// 存在并发修改异常的情况;
ArrayList list = new ArrayList();
list.add("java");
list.add("python");
list.add("android");
for (String string : list) {
if(string.equals("java")) {
//list.add("javaEE"); // ConcurrentModificationException
}
}
}
}
静态导入:导入到方法的级别.
格式: import static 包名—.类名.方法名;
注意事项:
A:导入的方法必须是静态的;
B:如果有多个同名的静态方法,使用方法时加上前缀(包名).
public class MyCollectionDemo {
public static void main(String[] args) {
// 创建对象,Collection是接口,利用其子接口的实现类测试
Collection c1 = new ArrayList();
// boolean add(Object obj)
System.out.println(c1.add("java"));
// void clear()
c1.clear();
c1.add("hello");
c1.add("world");
// boolean remove(Object o)
System.out.println(c1.remove("hello")); // 有该元素时返回true
System.out.println(c1.remove("somnus")); // 无该元素时返回false
// boolean contains(Object o)
System.out.println(c1.contains("java")); // 没有时返回false
System.out.println(c1.contains("world"));// 有则返回true
// boolean isEmpty()
System.out.println(c1.isEmpty()); // true
// int size()
System.out.println(c1.size()); // 返回集合存储的元素的个数
System.out.println(c1);
// 测试addAll,removeAll,iterator,retainAll,toArray方法
method();
}
// 测试addAll,removeAll,iterator,retainAll,toArray方法
public static void method() {
Collection c1 = new ArrayList();
Collection c2 = new ArrayList();
c1.add("ab1");
c1.add("ab2");
c1.add("ab3");
c2.add("ab3");
c2.add("ab4");
c2.add("ab5");
// addAll
//System.out.println(c1.addAll(c2)); // 将集合c2中元素添加进集合c1中,c2不变,返回true,
// removeAll
//System.out.println(c1.removeAll(c2)); // 去除c1和c2中相同的元素,若有相同的元素则去除并返回true
//System.out.println(c1.removeAll(c2)); // 若没有相同的元素,两个集合均不变,返回false
// retainAll
//System.out.println(c1.retainAll(c2)); // 求两个集合的交集,将结果存在c1中,如果c1集合发生变化,则返回true
// toArray
Object[] objs = c1.toArray(); // 可以将对象数组转换为字符串数组
System.out.println(Arrays.toString(objs));
// iterator
Iterator iterator = c1.iterator();
// hasNext方法时判断是否还有元素,如果不判断,直接使用next方法,当没有元素时抛出异常
while(iterator.hasNext()) { // 集合的迭代器遍历集合元素
// next方法时获取下一个元素,从第一个开始
System.out.println(iterator.next());
}
System.out.println("c1:"+c1);
System.out.println("c2:"+c2);
}
}
List集合是有
索引值
的集合接口,List接口继承Collection接口,拥有Collection定义的所有方法,有3个具体实现类,ArrayList类,Vector类和LinkedList类.
特点: 元素有序(存和取顺序相同),元素可重复,有索引.
注意事项: List集合中的有些方法(contains和remove)底层是是使用equals方法进行判断两个元素是否相等,则自定义对象需要重写equals方法,在equals方法中定义两个自定义对象是否相同的规则.
List接口继承至Collection集合接口,拥有Collection集合接口定义的所有的方法,下面只阐述List集合所特有的方法.
void add(int index,Object obj):向集合指定索引处添加一个元素.
boolean addAll(int index,Collection c):向集合中添加另一个集合的元素.
Object get(int index):获取指定索引处的元素.
int indexOf(Object o):获取集合中第一次出现指定元素的索引.
int lastIndexOf(Object o):获取集合中最后一次出现指定元素的索引.
ListIterator listIterator():迭代器.
List subList(int fromIndex,int toIndex):获取原集合中[fromIndex,toIndex)之间的元素.
E remove(int index):删除集合指定索引处的元素.
E set(int index,E element):替换指定索引处的元素,返回被替换的元素.
# 列表迭代器ListIterator:是List集合所特有的遍历方式;
# 特点:可以对集合元素进行逆序遍历(前提是先正向遍历一次).
# 并发修改异常:在使用迭代器遍历集合的同时使用集合对象修改数据.
# 解决方法:
* A:使用ListIterator迭代器遍历,使用ListIterator迭代器修改(List集合特有);
* B:使用集合的size()方法和get方法配合使用,遍历的同时修改数据;
ArrayList集合实现了List接口所有方法,具有List集合的所有特点.
# 底层数据结构是数组,查询速度快,增删数据慢,线程不安全,效率高.
LinkedList集合实现了List接口所有方法,具有List集合的所有特点.
# 底层数据结构是链表,查询速度慢,增删慢,线程不安全,效率高.
public void addFirst(E e):将指定元素添加至集合头部;
public void addLast(E e):将指定元素添加至集合末尾.
public E getFirst():获取集合的第一个元素;
public E getLast():获取集合的最后一个元素;
public E removeFirst():删除集合的第一个元素,并返回;
public E removeLast():删除集合中的最后一个元素,并返回.
public class MyList_LinkedListDemo {
public static void main(String[] args) {
LinkedList lin = new LinkedList();
lin.add("python");
lin.add("android");
// public void addFirst(E e)
lin.addFirst("java");
lin.addLast("hello");
// public E getFirst()
System.out.println(lin.getFirst());
// public E getLast():
System.out.println(lin.getLast());
// public E removeFirst()
System.out.println(lin.removeFirst());
// public E removeLast()
System.out.println(lin.removeLast());
System.out.println(lin);
// 案例
method();
}
/*
* 使用LinkedList类模拟栈的数据结构
*/
public static void method() {
MyStack ms = new MyStack();
ms.add("java");
ms.add("hello");
ms.add("world");
while(!ms.isEmpty()) {
System.out.println(ms.get());
}
}
}
// 定义栈结构类
class MyStack {
private LinkedList lin;
public MyStack() {
lin = new LinkedList();
}
public void add(Object obj) {
lin.addFirst(obj);
}
public Object get() {
return lin.removeFirst();
}
public boolean isEmpty() {
return lin.isEmpty();
}
}
**Vector**集合是Java语言早期的集合类,现在基本不使用,被ArrayList集合替代.
Vector类具有List集合接口的所有功能.
# 底层数据结构是数组,查询速度快,增删慢,线程安全,效率低.
public void addElement(E obj):将指定的组件添加到此向量的末尾,将其大小增加1.
public E elementAt(int index):返回指定索引处的组件.
public Enumeration elements():返回此向量的组件的枚举.
public class MyList_VectorDemo {
public static void main(String[] args) {
Vector ve = new Vector();
// public void addElement(E obj)
ve.addElement("abc");
ve.addElement("def");
ve.addElement("jdk");
// 遍历
System.out.println(ve); // [abc, def, jdk]
// public E elementAt(int index),index范围[0,集合长度+1]
System.out.println(ve.elementAt(2)); // jdk
//System.out.println(ve.elementAt(4)); // 异常
// public Enumeration elements():遍历集合元素
Enumeration elements = ve.elements();
while(elements.hasMoreElements()) {
System.out.println(elements.nextElement());
}
}
}
Set集合接口继承Collection集合接口,具有Collection接口定义的所有方法,有两个子类HashSet类(有一个LinkedHashSet子类)和TreeSet类.
特点:无序(存入和取出顺序可能不同),元素唯一(元素不能重复),没有索引值.
**注意事项:**Set集合不能使用普通for循环进行遍历,因为Set集合没有提供像List集合那样通过索引获取元素的方法.
Set集合接口继承至Collection集合接口,具有Collection接口的所有的功能方法.
HashSet:实现Set集合接口,由哈希表支持,本质是HashMap集合的实例(源代码).
# 特点:底层数据结构是哈希表(元素为链表的数组),综合了数组(查询速度快)和链表(增删快)数据结构的好处.
# A:它不保证set的迭代顺序,特别是它不保证该顺序恒久不变(存入和取出的顺序可能不相同),当存入顺序和取出顺序相同时,并不代表元素是有序的存入和取出.
# B:元素时唯一的,不能重复.
# C:集合没有索引值(List集合具有索引值).
* A:需要借助对象的hashCode()方法和equals()方法实现(对象需要重写这两个方法)
* B:HashSet集合内使用的是HashMap集合,HashSet集合的add方法本质是使用HashMap的put方法添加元素;
* HashMap的put方法添加元素步骤:
* 先进行对象的哈希值(通过对象的hashCode()方法获取)的比较,
* 如果两个对象的哈希值相同,则继续比较对象的地址值或者使用equals()方法比较
* 如果对象的地址值或者equals()比较的结果不同,则将将元素添加到集合
* 如果对象的地址值或者equals()比较的结果相同,则直接返回,不添加元素
* 如果不同就将该元素添加进集合.
* 注意:Object的equals()方法使用的==进行比较,如果子类没有重写该方法,则返回的一定是false.
* C:根据HashSet集合保证元素唯一性的原理,自定义对象必须重写Object类的hashCode方法和equals方法来保证.
# hashCode方法:对象的哈希值和对象的成员变量的值相关.
* 要求是尽可能的使对象的哈希值不同(如果对象的哈希值都相同,则只能通过equals方法保证元素的唯一性,则每次添加元素均会做该元素与之前的元素的比较,效率较低).
* 实现:成员变量的值相加(为保证哈希值的尽可能的不同,可以在其中一个成员变量的值乘以一个整数).
* 成员变量是基本数据类型,是值相加;
* 成员变量是引用数据类型,是该引用类型的hashCode()方法得到的值相加.
# equals方法:
* 标准如下:
* public boolean equals(Object obj) {
* if(obj == null) {
* return false;
* }
* if(this==obj) {
* return true;
* }
* if(!(obj instanceof Person)) {
* return false;
* }
* Person s = (Person)obj;
* return this.name.equals(s.getName()) && (this.age == s.getAge());
* }
# 在eclipse中:自定义对象的hashCode方法和equals方法使用自动生成.
public class MySetDemo {
public static void main(String[] args) {
// 创建hashset对象
HashSet s = new HashSet();
s.add("java");
s.add("pyhon");
s.add("android");
s.add("android");
for (String string : s) {
System.out.println(string);
}
// 创建set集合对象,添加自定义对象
HashSet ps = new HashSet();
ps.add(new Person("郭靖", 23));
ps.add(new Person("黄蓉", 22));
ps.add(new Person("洪七公", 23));
ps.add(new Person("黄蓉", 22));
for (Person person : ps) {
System.out.println(person.getName() + "..." + person.getAge());
}
}
}
// 自定义类
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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 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;
Person other = (Person) 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;
}
}
LinkedHashSet集合:是HashSet集合的子类,具有父类的所有功能方法.
# 底层数据结构是链表和哈希表组成.
# 特点:
* 元素唯一性(哈希表保证元素的唯一性);
* 元素有序(链表保证了元素的存取顺序一致);
* 与HashSet集合相比,功能方法均相同,只是底层数据结构不同.
元素的唯一性由哈希表保证,则当元素时自定义对象时,该对象所属的类需要重写hashCode和equals方法.
LinkedHashSet与HashSet的区别:
前者的数据是有序的(元素存取顺序一致),后者的数据是无序的(元素存取顺序不一致).
public class MySet_HashSet_LinkedHashSetDemo {
public static void main(String[] args) {
// LinkedHashSet存取数据的顺序相同
LinkedHashSet hs = new LinkedHashSet();
hs.add("java");
hs.add("python");
hs.add("android");
for (String string : hs) {
System.out.println(string);
}
// LinkedHashSet存储自定义类型数据
LinkedHashSet hs2 = new LinkedHashSet();
hs2.add(new Dog("二哈", 12));
hs2.add(new Dog("三哈", 12));
hs2.add(new Dog("二哈", 12));
for (Dog dog : hs2) {
System.out.println(dog.getName()+"..."+dog.getAge());
}
}
}
class Dog {
private String name;
private int age;
public Dog() {}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
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 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;
Dog other = (Dog) 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;
}
}
TreeSet集合:是Set集合的子类,本质是TreeMap的实例.
# 底层数据结构是红黑树(自平衡的二叉树).
# 特点:
* 元素是有序(自然排序和比较器排序);
* 元素是唯一的;
* 元素无序(存储顺序不一致).
TreeSet():构造一个新的空set,该set根据其元素的自然顺序进行排序.
TreeSet(Comparator comparator):构造一个新的空TreeSet,它根据指定比较器进行排序.
TreeSet的
add()
方法是调用TreeMap类的put()
方法,该put方法进行了数据的判断,舍弃了重复的元素.
# A:自然排序:使用的TreeSet的无参构造方法,元素本身必须实现Comparable接口.
* 元素对象对应的类必须实现Comparable接口(该接口表示的就是自然排序),并重写compareTo()方法,
* 在compareTo()方法中定义排序的规则(根据主要条件分析出次要条件).
# B:根据比较器排序:使用TreeSet的有参构造方法,其参数是一个Comparator接口的子类.
* 元素本身不许再实现Comparable接口,而是在创建TreeSet对象时传入实现了Comparator接口的子类,
* 该子类需重写Comparator中的compare()方法,在其中定义排序的规则(根据主要条件分析出次要条件).
# 两种排序方式的区别:当两种排序方式出现时,优先使用比较器排序.
* 自然排序:是将排序的规则定义在元素对应的类中,不灵活,当排序方式需求改变时,需要改变元素对应的类;
* 比较器排序:是将比较的规则作为TreeSet集合对象的参数(参数可以以匿名内部类的方式给出),方便灵活,由创建集合对象者给出排序规则.
public class MySet_TreeSetDemo {
public static void main(String[] args) {
// 使用自然排序,创建TreeSet的无参构造方法
// 需要知道自定义对象实现Comparable接口,按照年龄排序
TreeSet ts = new TreeSet();
ts.add(new Student("郭靖", 28));
ts.add(new Student("郭靖", 23));
ts.add(new Student("郭靖", 23));
ts.add(new Student("黄蓉", 25));
ts.add(new Student("黄蓉", 30));
ts.add(new Student("洪七公", 25));
for (Student student : ts) {
System.out.println(student.getName()+"..."+student.getAge());
}
// 比较器排序方式
// 创建TreeSet集合时定义规则
TreeSet ts1 = new TreeSet(new Comparator() {
// 用多态和内部类的方式创建Comparator比较器
@Override
public int compare(Car c1, Car c2) {
// 定义比较规则
// c1是代表新添加的元素,c2代表是已经存在的元素
// c1减去c2,代表是从小到大
// c2减去c1,代表是从大到小
//根据源码得知:
// 如果return 0,则表示所有元素均重复,只有保留根元素(第一个添加的元素)
// 如果return 负数,从小到大排序
// 如果return 正数,从大到小排序
// 主要条件,价格
int num = c1.getPrice() - c2.getPrice();
// 次要条件,颜色
int num2 = num==0?c1.getColor().compareTo(c2.getColor()):num;
// 次要条件,品牌
int num3 = num2==0?c1.getBrand().compareTo(c2.getBrand()):num2;
return num3;
}
});
// 添加元素
ts1.add(new Car("宝马", "黑色", 20000));
ts1.add(new Car("奔驰", "黑色", 15000));
ts1.add(new Car("宝马", "黑色", 20000));
ts1.add(new Car("大众", "白色", 20000));
ts1.add(new Car("比亚迪", "黑色", 20000));
for (Car car : ts1) {
System.out.println(car.getBrand()+"..."+car.getColor()+"..."+car.getPrice());
}
}
}
/*
* 定义自定义类
* 注意事项:使用TreeSet集合需要指定进行自然排序的规则(要分析出次要的规则)
* 即该类需要实现comparable接口
*/
class Student implements Comparable {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
/*
* 重写compareTo方法
* 根据主要条件需要分析出次要条件,即需要将整个对象的所有成员变量均比较完,才能判断是否相同
*/
@Override
public int compareTo(Student s) {
// 主要条件是按照年龄排序
// this代表是新添加的元素,s代表是已经存在的元素
// this减去s,代表是从小到大
// s减去this,代表是从大到小
//根据源码得知:
// 如果return 0,则表示所有元素均重复,只有保留根元素(第一个添加的元素)
// 如果return 负数,从小到大排序
// 如果return 正数,从大到小排序
int num = this.age - s.age;
// 分析次要条件,年龄相同并不代表这两个对象就相同,还要比较姓名
// 如果年龄相同,再比较姓名是否相同,比较姓名是否相同是使用的String类的compareTo方法
int num2 = num == 0?this.name.compareTo(s.name):num;
return num2;
}
}
/*
* 自定义类
* 测试比较器排序
*/
class Car {
private String brand;
private String color;
private int price;
public Car() {}
public Car(String brand, String color, int price) {
this.brand = brand;
this.color = color;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}