Java学习计划07:集合、泛型

目录

  • Collection
  • List
  • Set
  • 泛型
  • Map
  • Collections

Collection

  • collection是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素。
  • JDK不题库此接口的任何直接实现,提供更具体的子接口(如Set和List)实现

创建Collection集合的对象

  • 多态方式
  • 具体的实现类arrayList
方法名 说明
add(E e) 添加元素
remove(Object o) 从集合中移除指定的元素
void clear() 清空集合中的元素
contains(Object o) 判断集合中是否存在指定的元素
isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数

Iterator:迭代器

集合的专用遍历方式

 //创建集合对象
Collection<String> c = new ArrayList<String>();
//获取迭代器方法
Iterator<String> it = c.iterator();
  • Iterator iterator(); 返回此集合中元素的迭代器,通过集合的iterator()方法得到
  • 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的

Iterator中常用方法:

  • E next():返回迭代器中的下一个元素
  • hasNext():如果迭代具有更多元素,则返回true

代码演示

public class CollectionDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> c = new ArrayList<String>();
        //add(E e)添加元素
        c.add("hello");
        c.add("world");
        c.add("java");
        //获取迭代器方法
        Iterator<String> it = c.iterator();
        //while 循环判断it.hasNext()是否存在元素,如果有输出元素
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
    }
}

案例:Collection集合存储学生对象并遍历

需求:创建一个存储学生对象的集合,存储3个学生对象,并遍历

//测试学生类
public class TestStudent {
    public static void main(String[] args) {
        //创建collection集合对象
        Collection<Student> student = new ArrayList<Student>();
        //创建学生对象
        Student s1 = new Student("赵乐乐",20);
        Student s2 = new Student("李雷雷",22);
        Student s3 = new Student("韩梅梅",19);
        //把学生添加到集合
        student.add(s1);
        student.add(s2);
        student.add(s3);
        //遍历集合
        Iterator<Student> it = student.iterator();
        while (it.hasNext()){
            Student s = it.next();
            System.out.println("姓名:"+s.getName()+",年龄:"+s.getAge());
        }
    }
}
//学生类
public class Student {
    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;
    }
}

List

List集合概述

  • 有序集合(也称为序列),用户可以准确控制列表中的每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素。
  • 与Set集合不同,列表通常允许重复的元素。

List集合的特点:

  • 有序:存储和取出的元素顺序一致
  • 可重复:存储的元素可以重复

List集合特有方法

方法名 说明
void add(int index,E element) 在此集合中的制定位置超如制定元素
remove(int index) 删除指定索引处的元素,返回被删除的元素
set(int index,E element) 修改指定索引处的元素,返回被修改的元素
get(int index) 返回指定索引处的元素

案例:List集合存储学生对象并遍历**(三种方式遍历 迭代器方式,for循环,for each遍历)**

需求:创建一个存储学生对象的集合,存储3个学生对象,并遍历

Student类和上面Collection类相同,小编就不写了,直接写TestList类,主要是多练习和掌握着两种遍历方式。

public class TestList {
    public static void main(String[] args) {
        //创建List集合对象
        List<Student> student =new ArrayList<Student>();
        //创建学生对象
        Student s1 = new Student("赵乐乐",20);
        Student s2 = new Student("李雷雷",22);
        Student s3 = new Student("韩梅梅",19);
        //把学生添加到集合
        student.add(s1);
        student.add(s2);
        student.add(s3);
        //迭代器方式遍历
        Iterator<Student> it = student.iterator();
        while (it.hasNext()){
            Student s = it.next();
            System.out.println("姓名:"+s.getName()+"年龄:"+s.getAge());
        }
         System.out.println("--------------------------");
       //for循环方式
        for (int i = 0; i < student.size(); i++) {
            Student s =student.get(i);
            System.out.println("姓名:"+s.getName()+"年龄:"+s.getAge());
        }
         System.out.println("--------------------------");
        //for each遍历
        for (Student s:student) {
            System.out.println("姓名:"+s.getName()+"年龄:"+s.getAge());
        }
    }
}

并发修改异常

//ConcurrentModificationException

产生原因

  • 迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致

解决方案

  • 用for循环遍历,然后用集合对象做对应的操作即可

ListIterator:列表迭代器

  • 通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器

  • 用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置

ListIterator中的常用方法

  • E next():返回迭代器的下一个元素
  • hasNext():如果迭代具有更多元素,则返回true
  • E previous():返回列表中的上一个元素
  • hasPrevious():如果此列表迭代器在相反方向遍历列表是具有更多元素,则返回true
  • void add(E e) : 将制定的元素插入列表

List集合子类特点

List集合常用子类:ArrayList ,LInkedList

  • ArrayList:底层数据结构是数组,查询快,增删慢
  • LinkedList:底层数据结构是链表,查询慢,增删块

ArrayList和LinkedList分别用三种方式遍历数组元素

public class LinkedList {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<String> array = new ArrayList<String>();
        array.add("hello");
        array.add("world");
        array.add("java");
        System.out.println("ArrayList三种遍历方式");
        //for each遍历
        System.out.println("for each遍历");
        for (String s:array) {
            System.out.println(s);
        }

        //for循环遍历
        System.out.println("for 循环遍历");
        for (int i = 0; i < array.size(); i++) {
            String s = array.get(i);
            System.out.println(s);
        }
        //迭代器遍历
        System.out.println("迭代器遍历");
        Iterator<String> it = array.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("----------------------------");
        System.out.println("LinkedList三种遍历方式");
        java.util.LinkedList<String> linkedList= new java.util.LinkedList<>();
        linkedList.add("hello");
        linkedList.add("world");
        linkedList.add("java");
        //for each 遍历
        System.out.println("for each遍历");
        for (String s:linkedList) {
            System.out.println(s);
        }

        //for循环遍历
        System.out.println("for 循环遍历");
        for (int i = 0; i < linkedList.size(); i++) {
            String s = linkedList.get(i);
            System.out.println(s);
        }
        //迭代器遍历
        System.out.println("迭代器遍历");
        Iterator iterator =linkedList.iterator() ;
        while (iterator.hasNext()){
            String s1 = (String) iterator.next();
            System.out.println(s1);
        }

    }
}

LinkedList和ArrayList的添加10000元素耗时对比

import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
public class LinkedListDemo01 {
    public static void main(String[] args) {
        List<String> LList = new LinkedList<String>();
        List<String> AList = new ArrayList<String>();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            LList.add(""+i);
        }
        long endTime = System.currentTimeMillis();
        long result = endTime - startTime;
        System.out.println("LinkedList添加耗时:"+result);
        for (int i = 0; i < 10000; i++) {
            AList.add(""+i);
        }
        endTime = System.currentTimeMillis();
        result=endTime = startTime;
        System.out.println("ArrayList添加耗时:"+result);
    }
}

Set

Set集合特点

  • 不包含重复元素的集合
  • 没有带索引的方法,所有不能使用普通for循环遍历
  • 对集合迭代顺序不做任何保证,不保存储和取出的顺序一致

哈希值

哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中有一个方法可以获取对象的哈希值

  • public int hashCode():返回对象的哈希码值

对象的哈希值的特点:

  • 默认情况下,不同对象的hashcode()值是不同的
  • 通过方法重写,可以实现不同对象的哈希值是相同的

HashSet

HashSet集合特点

  • 继承Set集合特点
  • 底层数据结构是哈希表

HashSet集合存储元素,要保证元素唯一性,需要重写hashCode()和 equals()

//重写hashCode() equals()
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Student student = (Student) o;
    return age == student.age && Objects.equals(name, student.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

哈希表

  • JDK8 之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
  • JDK8以后,在长度比较长的时候,底层实现了优化
    Java学习计划07:集合、泛型

LinkedHashSet

  • 哈希表和链表实现的Set接口,具有可预测的迭代次序
  • 有链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  • 有哈希表保证元素唯一,也就是说没有重复的元素

代码演示

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();
        linkedHashSet.add("hello");
        linkedHashSet.add("world");
        linkedHashSet.add("java");
        linkedHashSet.add("world");//重复元素
        for (String s:linkedHashSet) {
            System.out.println(s);
        }
    }
}

Java学习计划07:集合、泛型

TreeSet

  • 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
    • TreeSet():根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator):根据指定的比较器进行排序
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以不包含重复元素的集合

自然排序Comparable的使用

  • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
public class Student implements Comparable<Student>  {
    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;
    }

    @Override
    public int compareTo(Student s) {
//        return 0;
//        return 1;
//        return -1;
//        按照年龄从小到大
//        int num =s.age- this.age;//降序
          int num = this.age-s.age;//升序
          //年龄相同时,按姓名字母升序
          int num2 =  num==0?this.name.compareTo(s.name):num;
          return num2;
    }
}
import java.util.TreeSet;public class TreeSetComparable {    public static void main(String[] args) {        //创建集合对象        TreeSet<Student> ts  = new TreeSet<Student>();        Student s1 = new Student("diaochan",22);        Student s2 = new Student("wangzhaojun",24);        Student s3 = new Student("yangyuhuan",20);        Student s4 = new Student("xishi",23);        Student s5 = new Student("wangsimin",23);        ts.add(s1);        ts.add(s2);        ts.add(s3);        ts.add(s4);        ts.add(s5);        for (Student s:ts) {            System.out.println(s.getName()+s.getAge());        }    }}

结论

  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 自然排序,就是让元素所属的列实现Comparable接口,重写comparaTo(To)方法
  • 重写方法是一定要注意排序规则必须按照要求的朱啊哟条件和次要条件来写

案例:编写一个程序获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出。

import java.util.HashSet;import java.util.Random;import java.util.Set;import java.util.TreeSet;public class TreeSetDemo02 {    public static void main(String[] args) {        //1、创建Set集合//       Set<Integer> set = new HashSet<Integer>(); //无序的        Set<Integer> set = new TreeSet<Integer>();//有序的        //2.创建随机数对象        Random r = new Random();        //3.判断集合长度是否小于10        while (set.size()<10){            //产生一个随机数,添加到集合           int number= r.nextInt(20)+1;           set.add(number);        }        //遍历集合        for (Integer i:set) {            System.out.println(i);        }    }}

泛型

泛型:是JDK5中引入的特性,他提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型

它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数

一提到参数,最熟悉的就是定义方法时有形参,然后调用次方法时传递实参。那么参数化类型该怎么理解呢?

顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型

泛型定义格式:

  • <类型>:指定一种类型的格式。这里的类型可以看成形参
  • <l类型1,类型2,…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
  • 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型。

泛型的好处:

  • 类的成员变量数据可以在创建对象的时候再去指定

  • 把运行时期的问题提前到了编译期间

  • 避免了强制类型转换

泛型类

泛型类的定义格式:

  • 修饰符 class 类名<类型>{}
  • public class Generic{}
    • 此处的T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用与标识泛型
//泛型类
public class Generic <T>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
//泛型类的使用
Generic<String> g1 = new Generic<String>();
g1.setT("赵磊磊");
System.out.println(g1.getT());
Generic<Integer> g2 = new Generic<Integer>();
g2.setT(30);
System.out.println(g2.getT());

泛型方法

  • 格式: 修饰符 <类型>返回值类型 方法名(类型 变量名){}
  • public void show(T t){}

泛型方法定义

//泛型方法
public class GenericDemo01{
    public <T> void show(T t){
        System.out.println(t);
    }
}

泛型方法使用

GenericDemo01 g1 = new GenericDemo01();
g1.show("李雷雷");
g1.show(30);
g1.show(true);
g1.show(12.33);

泛型接口

  • 格式:修饰符 interface 接口名<类型>{ }
  • 范例:public interface Generic{ }

代码演示

泛型接口定义

public interface Generic<T>{
    void show(T t);
}

实现泛型接口方法的类

public class Genericlmpl<T> implements Generic<T> {

    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

测试类

public class GenericDemo {
    public static void main(String[] args) {
        Generic<String> g1 = new Genericlmpl<String>();
        g1.show("李雷雷");
        Generic<Integer> g2 = new Genericlmpl<Integer>();
        g2.show(12);

    }
}

类型通配符

为了表示各种泛型List的父类,可以使用类型通配符

  • 类型通配符:< ?>
  • List< ?>:表示元素类型未知的List,它的元素可以匹配任何的类型
  • 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中

如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限

  • 类型通配符上限:<?extends 类型>
  • List<?extends Number>:它表示的类型是Number或者其子类型-
  • 类型通配符的上限就是可定义的最高类型
 //类型通配符的上限
//        List<? extends Number> list4 = new ArrayList<Object>();
        List<? extends Number> list4 = new ArrayList<Integer>();

除了可以指定类型通配符的上限,我们也可以指出类型通配符的下限

  • 类型通配符下限:<?super 类型>

  • List<?super Number>:它表示的类型是Number或者其父类型

  • 类型通配符的下限就是可定义的最低类型

 //类型通配符的下限
        List<? super Number> list5 = new ArrayList<Object>();
        List<? super Number> list6 = new ArrayList<Number>();
//        List<? super Number> list7 = new ArrayList<Integer>();
    }

可变参数

可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了

  • 格式:修饰符 返回值类型 方法名(数据类型… 变量名){ }
  • 范例:public static int sum(int…a){ }

可变参数的注意事项

  • 这里的变量其实是一个数组
  • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后

可变参数求和代码演示

public class ArgsDemo01 {
    public static void main(String[] args) {
        System.out.println(sum(10,30,40,50,60));
        System.out.println(sum(10,30));
        System.out.println(sum(10,2,3,3,5,60));
    }
    public static int sum(int...a){
     int sum = 0;
        for (int i : a) {
            sum += i;
        }
        return sum;
    }
}

Map

  • Interface Map<K,V> K:键的类型;V:值的类型
  • 将键映射到值的对象;不能包含重复的键;每个键可以映射到最多一个值
  • 举例 学生的学号 和 姓名
    • ineia001 李雷雷
    • ineia002 赵晶晶
    • ineia003 王乐乐

创建Map集合的对象

  • 多态的方式
  • 具体的实现类HashMap

Map集合的基本功能

方法名 说明
put(K key,V value) 添加元素
remove(Object key) 根据键删除键值对应的元素
void clear() 移除所有的键值对元素
containsKey(Object key) 判断集合是否包含指定的键
containsValue(Object value) 判断集合是否包含指定的值
isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中键值对的个数
get(Object key) 根据键获取值
map.keySet() 获取数组中所有键的集合
map.values() 获取数组中所有值的集合
map.entrySet() 获取所有键值对的集合

两种遍历map集合的方法

public class MapDemo03 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<String,String>();
        map.put("张无忌","赵敏");
        map.put("杨过","小龙女");
        map.put("郭靖","黄蓉");
        Set<String> KeySet = map.keySet();
        for (String k:KeySet) {
            String value = map.get(k);
            System.out.println(k +value);
        }
    }
}
public class MapDemo04 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<String,String>();
        map.put("张无忌","赵敏");
        map.put("杨过","小龙女");
        map.put("郭靖","黄蓉");
        //获取所有键值对对象的集合
        Set<Map.Entry<String, String>> enTrySet = map.entrySet();
        //遍历所有键值对对象,获取每一对象
        for (Map.Entry<String, String> ma:enTrySet) {
            //根据键值对对象获取键和值
            System.out.println(ma.getKey()+ma.getValue());
        }

    }
}

统计字符串中每个字符出现的次数

举例:键盘录入“aababcabcdabcde" 在控制台输出:a(5)b(4)c(3)d(2)e(1)

import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;

public class MapDemo07 {
    public static void main(String[] args) {
        //键盘录入一个字符串
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String line = scanner.nextLine();
        //创建HashMap集合,键是Character,值Integer
        HashMap<Character,Integer> hm = new HashMap<Character,Integer>();
        //遍历字符串,得到每一个字符
        for (int i = 0; i < line.length(); i++) {
            char key = line.charAt(i);
            //拿得到的每一个字符作为键到HashMap集合中去找对应的值,看其返回值
            Integer value = hm.get(key);
            if (value == null){
                //如果返回值是null,则说明该字符在HashMap集合中不存在,该字符作为key,1作为存储值
                hm.put(key,1);
            }else{
                value++;
                hm.put(key,value);
            }
        }
        //遍历HashMap集合,得到键和值,按照要求拼接
        StringBuilder sb = new StringBuilder();
        Set<Character> keySet = hm.keySet();
        for (Character key:keySet) {
            Integer value = hm.get(key);
            sb.append(key).append("(").append(value).append(")");
        }
        String result =sb.toString();
        //输出结果
        System.out.println(result);
    }
}

Collections

Collections类的概述

  • 是针对集合操作的工具类

Collections类的常用方法

  • sort(List list):将指定的列表按升序排序

  • reverse(List<?>list):反转指定列表中元素的顺序

  • shuffle(List<?> list):使用默认的随机源随机排列指定的列表

上一篇:07 HBase操作


下一篇:C++函数适配器