集合框架
在没有学集合框架之前,我们存多个数据只会用数组,但是学了集合类后,我们存储多个数据就可以直接调用。
在总结集合笔记之前,我们先学泛型,以泛型进入集合会更好理解。
泛型:
一、泛型的含义:
1、广泛通用的类型。
2、代码模板中类型不确定,谁调用该段代码,谁指明类型是什么。
二、为什么要使用泛型?
1、存储任意类型的数据在集合中 ,但是取出来都是Object类型的,此时就得强转,使用泛型避免了数据类型转换的异常。
2、约束存储到集合中的元素必须是相同的数据类型(相同的数据类型才能做比较,比如TreeSet类)。
3、设计一个点(Point)类,来封装坐标位置,要求坐标位置支持引用数据类型(String类型.Integer类型/Double类型)。
三、泛型的使用:
1、保证前后类型相同。
2、泛型与子类继承的限制(在泛型中不能用父类来接受子类)。
3、泛型类中的泛型只能适用于非静态方法。
4、型类中的泛型应该适用于整个类中多个方法,有时候只对某一个方法设置泛型即可。
Demo:
public class GeneralDemo2 {
public static void main(String[] args) {
System.out.println(MethodDemo.test("huairen"));
Person1<String,Car<Integer>> p = new Person1<String,Car<Integer>>("张浩", new Car<Integer>(2));
System.out.println(p.getTemp1()+p.getTemp2());
}
}
//静态方法不能使用类上定义的泛型
class MethodDemo<V>{
public static <T> T test(T temp){
return temp;
}
//错误
/*public static void test2(V temp){
System.out.println(temp);
}*/
public <T> T[] test3(T[] temp){
return temp;
}
}
class Person1<V,T>{
private V temp1;
private T temp2;
public Person1(V temp1, T temp2) {
this.temp1 = temp1;
this.temp2 = temp2;
}
public V getTemp1() {
return temp1;
}
public void setTemp1(V temp1) {
this.temp1 = temp1;
}
public T getTemp2() {
return temp2;
}
public void setTemp2(T temp2) {
this.temp2 = temp2;
}
}
class Car<V>{
private V temp;
public Car(V temp) {
this.temp = temp;
}
public V getTemp() {
return temp;
}
public void setTemp(V temp) {
this.temp = temp;
}
@Override
public String toString() {
return "此车"+temp+"轮胎";
}
}
四、通配符:
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知此时只能接受数据,不能往该集合中存储数据。
泛型的上限和下限:用来限定元素的类型必须是X类的子类或相同, X的父类或相同。
Demo:
public class GeneralDemo {
public static void main(String[] args) {
//1.泛型中不能使用基础数据类型,只能使用包装类
//泛型中不能使用父类接收子类
Point<Integer,String> p2 = new Point<Integer,String>(2,"北纬22度");
System.out.println(p2);
new Person().getPoint(p2);
}
}
class Person{
//extends相当于小于或等于
//super相当于大于等于
public void getPoint(Point<?extends Number,?super String> p){
System.out.println("人在"+p);
}
}
class Point<T,V>{
private T x;
private V y;
public Point(T x, V y) {
this.x = x;
this.y = y;
}
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public V getY() {
return y;
}
public void setY(V y) {
this.y = y;
}
public String toString() {
return "("+x+","+y+")";
}
}
五、泛型数组:
Demo:
public class GenericsDemo {
public static void main(String[] args) {
Integer[] a = {1,2,3,4,5};
GenericsArrays ga = new GenericsArrays();
ga.test(a);
Integer[] b = ga.testReturn(a);
for (int i = 0; i < b.length; i++) {
System.out.println(b[i]);
}
}
}
class GenericsArrays{
public<T> void test(T a[]) {
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
System.out.println("-------");
}
public<T> T[]testReturn(T a[]){
return a;
}
}
----------------------------------------------------------------
集合:
一、什么是集合?
集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法(底层都对应着某一种数据结构的算法)。
集合也就是一个动态的对象数组,可以不限制定义,不像数组那样有长度限制。
二、常用的框架接口规范:
1、集合中存储的对象,称之为集合元素;
2、集合中只能存储任意类型的对象;
3、集合中只能存储对象,不能存储基本数据类型的值;
4、集合类中存储的对象,都存储的是对象的引用,而不是对象本身.
三、常用的集合类:
1、Set(集):集合中的对象不按特定方式排序,不允许元素重复.
2、 List(列表):集合中的对象按照索引位置排序,允许元素重复.
3、Map(映射):集合中每一个元素都包含一对key和value对象.不允许key对象重复,值对象可以重复
四、集合类的操作方法:
1、增加:
A) boolean add(Object e) 将指定元素添加到此向量的末尾,等价于addElement方法。
B) void add(int index, Object element) 在此向量的指定位置插入指定的元素。
C) boolean addAll(Collection c) :把c集合中的元素添加到当前集合对象中.
2、删除:
A) Object remove(int index) :删除指定索引位置的元素,并返回删除之后的元素.
B) boolean remove(Object o):删除指定的元素.
boolean removeAll(Collection c):从此集合中移除包含在指定 集合c中的所有元素。
C) boolean retainAll(Collection c):在此集合中仅保留包含在指定 集合c中的元素,求两个集合的交集。
3、修改:
A) Object set(int index, Object element) :修改当前集合中指定索引位置的元素。 返回被替换的旧的元素.
4、查询:
A) int size() :返回当前集合中存储几个元素.
B) boolean isEmpty():如果当前集合中不包含指定元素,是则返回true.
C) boolean contains():如果当前集合中包含指定元素,是则返回true.
D) Object get(int index):查询指定索引位置的元素.
E) Object[] toArray():把集合对象转换为Object数组.
五、List类
特点:1)可以重复放入元素,只会删除集合中最先找到的元素;
2)记录元素的先后添加顺序。
ArrayList类:
1、ArrayList类相当于vector类是一个Object数组,是Java集合框架出现之后用来取代Vector类的,二者底层原理都是基于数组的算法,一模一样。
2、区别:
A) Vector: 所有的方法都使用了synchronized修饰符. 线程安全但是性能较低. 适用于多线程环境.
B) ArrayList:所有的方法都没有使用synchronized修饰符. 线程不安全但是性能较高.
即使以后在多线程环境下,我们也不使用Vector类:
ArrayList list = Collections.synchronizedList(new ArrayList(...));
LinkedList子类(一个链表操作类):
1、定义:LinkedList类是双向链表,单向队列,双向队列,栈的实现类:
2、LinkedList类实现单向队列和双向队列的接口,自身提高了栈操作的方法,链表操作的方法.
3、在LinkedList类中存在很多方法,但是功能都是相同的.LinkedList表示了多种数据结构的实现,每一种数据结构的操作名字不同。
4、LinkedList擅长保存和删除操作。
Demo:
public class LinkedListDemo {
public static void main(String[] args) {
//创建链表
LinkedList<String> ld = new LinkedList<String>();
ld.add("苹果1");
ld.add("苹果2");
ld.add("苹果3");
ld.add("苹果4");
ld.add("苹果5");
System.out.println(ld);
//在链表首尾添加
ld.addFirst("苹果0");
ld.addLast("苹果6");
System.out.println(ld);
//找到链表的头
System.out.println(ld.element());
System.out.println(ld.peek());
System.out.println(ld.poll());
System.out.println(ld);
//按照先进先出的思想取出链表里的元素
for(int i=0;i<6;i++){//注意这里不能写成 ld.size();因为ld.size()的值在随着链表里的元素被移除而变化
System.out.println(ld.poll()+":"+i);
}
}
//element:找到头;
//peek:找到头;
//poll:找到头并删除头;
}
七、迭代器(Iterator):
1、迭代器对象:
A) Iterator: 迭代器对象,只能从上往下迭代.
B) boolean hasNext(); 判断当前指针后是否有下一个元素
C) Object next():获取指针的下一个元素,并且移动指针.
D) ListIterator: 是Iterator接口的子接口,支持双向迭代,从上往下迭代,从下往上迭代.
Demo:
public class IteratorDemo {
public static void main(String[] args) {
List<Apple> a = new ArrayList<Apple>();
a.add(new Apple("a1"));
a.add(new Apple("a2"));
a.add(new Apple("a3"));
a.add(new Apple("a4"));
a.add(new Apple("a5"));
Iterator<Apple> it = a.iterator();
while(it.hasNext()) {
System.out.println(it.next()+",");
}
}
}
class Apple{
private String name;
public Apple(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "Apple [name=" + name + "]";
}
}
八、Set类:
特点:1):不允许元素重复(采用散列存储);
2):不会记录元素的先后添加顺序;
3):Set判断两个对象是否相等用equals,而不是使用==。也就是说两个对象equals比较返回true,Set集合是不会接受这个两个对象的。
Hashset类:
1、不保证元素的先后添加顺序.
2、底层才有的是哈希表算法,查询效率极高.
3、判断两个对象是否相等的规则:
a):equals比较为true.
b):hashCode值相同.
要求:要求存在在哈希中的对象元素都得覆盖equals和hashCode方法.
HashSet做等值查询效率高,TreeSet做范围查询效率高.
Demo:
public class TreeSetDemo1 {
public static void main(String[] args) {
HashSet<Dog> ts = new HashSet<Dog>();
ts.add(new Dog("狗1", 6));
ts.add(new Dog("狗2", 4));
ts.add(new Dog("狗2", 4));
ts.add(new Dog("狗2", 4));
ts.add(new Dog("狗3", 2));
ts.add(new Dog("狗4", 5));
System.out.println(ts);
}
}
class Dog{
private String name;
private int age;
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;
}
public String toString() {
return (this.name + ":" + this.age);
}
public boolean equals(Object obj){
//首先判断是不是我们要判断的类型
if(!(obj instanceof Dog)){
return false;
}
//判断是不是不同名字,但是指向了同一个
if(this == obj ){
return true;
}
//判断是不同的对象,但是值确是一样
Dog d = (Dog)obj;
if(this.age == d.getAge()&&this.getName()==d.getName()){
return true;
}else{
return false;
}
}
public int hashCode(){
return this.name.hashCode();
//return this.name.hashCode()*this.age;
}
}
TreeSet类:
1、不保证元素的先后添加顺序,但是会对集合中的元素做排序操作.
2、底层才有红黑树算法(树结构,比较擅长做范围查询).
TreeSet要么才有自然排序,要么定制排序.
A) 自然排序: 要求在TreeSet集合中的对象必须实现java.lang.Comparable接口,并覆盖compareTo方法.
B) 定制排序: 要求在构建TreeSet对象的时候,传入一个比较器对象(必须实现java.lang.Comparator接口).在比较器中覆盖compare方法,并编写比较规则.
3、TreeSet判断元素对象重复的规则:
compareTo/compare方法是否返回0.如果返回0,则视为是同一个对象.
Demo:
public class TreeSetDemo {
public static void main(String[] args) {
/*TreeSet<Animal> ts = new TreeSet<>();
ts.add(new Animal("dog1",2));
ts.add(new Animal("cat2",1));
ts.add(new Animal("bird3",4));
ts.add(new Animal("dog4",2));
System.out.println(ts);*/
//[Animal [name=cat2, age=1], Animal [name=dog1, age=2], Animal [name=bird3, age=4]]
Animal[] a = {new Animal("dog1",2),
new Animal("cat2",1),
new Animal("cat3",1),
new Animal("dog4",3)
};
Arrays.sort(a);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
//Animal [name=cat2, age=1]
Animal [name=cat3, age=1]
Animal [name=dog1, age=2]
Animal [name=dog4, age=3]
}
}
class Animal implements Comparable<Animal>{
private String name;
private Integer age;
Animal(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public int compareTo(Animal a) {
if (this.age > a.getAge()) {
return 1;
}else if(this.age < a.getAge()) {
return -1;
}else {
return 0;
}
}
@Override
public String toString() {
return "Animal [name=" + name + ", age=" + age + "]";
}
}
九、Map类(映射):
1、Map的常用实现类:
A) HashMap: 采用哈希表算法, 此时Map中的key不会保证添加的先后顺序,key也不允许重复. key判断重复的标准是: key1和key2是否equals为true,并且hashCode相等.
B) TreeMap: 采用红黑树算法,此时Map中的key会按照自然顺序或定制排序进行排序,,key也不允许重复. key判断重复的标准是: compareTo/compare的返回值是否为0.
C) LinkedHashMap: 采用链表和哈希表算法,此时Map中的key会保证先后添加的顺序,key不允许重复. key判断重复的标准和HashMap中的key的标准相同.
【注】HashMap和TreeMap以及LinkedHashMap都是线程不安全的,但是性能较高:
解决方案: Map m = Collections.synchronizedMap(Map对象);
Hashtable类实现线程安全的,但是性能较低.
哈希表算法:做等值查询最快.
数结构算法:做范围查询最快-->应用到索引上.
2、一般的,我们定义Map,key都使用不可变的类(String),把key作为value的唯一名称.
Demo:
public class MapDemo {
public static void main(String[] args) {
//利用Iterator输出Map(Map一般都是供查询,很少直接输出,但是一旦要输出必须按一下标准格式)
/*Map<String, Object> map = new HashMap<String,Object>();
map.put("lisi", "川大");
map.put("zhangsan", "电子科大");
map.put("wangwei", "北大");
Set<Map.Entry<String, Object>> entrys = map.entrySet();
for (Map.Entry<String, Object> entry : entrys) {
String key = entry.getKey();
Object values = entry.getValue();
System.out.println(key+"<--->"+values);
}*/
//需求:计算一个字符串中每个字符出现的次数:
String str = "helowjfjsvnfdjfdfdddhsefew";
//字符串本质是char[]
char[] arr = str.toCharArray();
Map<Character, Integer> map = new TreeMap<>();
for (char ch:arr) {
//判断当前字符是否在Map中的key存在
if (map.containsKey(ch)) {
//当前Map的key包含该字符,取出value值递增1,再存放进去
Integer old = map.get(ch);
map.put(ch, old+1);
} else {
//当前Map的key不包含该字符,取出value值为1
map.put(ch, 1);
}
}
System.out.println(map);
}
}
十、List、Set和Map的选用:
1、选用哪一种容器取决于每一种容器的存储特点以及当前业务的需求:
A) List: 单一元素集合.
允许元素重复/记录元素的添加顺序.
B) Set:单一元素集合.
不允许元素重复/不记录元素的添加顺序.
既要不重复,又要保证先后顺序:LinkedHashSet.
C) Map: 双元素集合. 如果存储数据的时候,还得给数据其为一个的一个名称,此时考虑使用Map.
2、List和Set以及Map之间相互转换问题:
List<String> list = new ArrayList<>();
把List转换为Set:
Set<String> set = new HashSet<>(list);//此时会消除重复的元素.
把Set转换为List:
List<String> list2 = new ArrayList<>(set );
Map不能直接转换为List或Set(但是Map中的方法可以间接转换).
十一、集合工具类(Collections)
1、为集合添加内容
List<String> all= new ArrayList<String>();
Collections.addAll(all,"haoren","huairen","person";
2、反转
collections.reverse(all);
3、替换
collections.replaceAll(all,"haoren","mz");
4、排序
collections.sort(all);
5、查找
collections.binarySearch(all,“haoren”);(先要排序,因为使用二分法)
6、交换指定位置的内容
collections.swap(all,1,2);