4、单双列集合

一、定义
集合只用于存储对象,集合长度是可变的,集合可以储存不同类型的数据,集合中存储的是对象的引用(地址值)。
二、Collection接口(单列集合顶层接口)

  • 集合与数组的特点
    集合:
    a、长度可变;
    b、只能储存引用数据类型;
    c、同一个集合中可以存储多种数据类型
    数组:
    a、长度不可变;
    b、可以存储基本数据类型和引用数据类型;
    c、同一个数组只能存储一种数据类型。
  • 集合的体系结构图(顶层)Collection(接口)
    a:List(接口)
    • ArrayList(集合:底层使用长度可变的数组数组数据结构,特点查询速度快但增删元素慢,线程不安全,效率高)
    • Vector(集合:底层使用数组数据结构,线程安全,效率低,被ArrayList取代)
    • LinkList( 集合:底层使用链表数据结构,特点增删快,查询稍慢,线程不安全,效率高)
    b:set(接口)
    • HashSet(集合:底层数据结构是Hash表,线程是非同步的。)
    • TreeSet(集合:层是二叉树数据结构)
三、Collection接口中的功能概述
  • 添加元素
    boolean add( Object obj ); //添加一个元素
    boolean addAll( Collection c )//添加一个集合的所有元素
  • 删除元素
    void clear( ); //清空集合
    boolean remove ( Object obj )//删除集合中的指定元素
    boolean removeAll( Collection c)//从集合中删除一个指定集合的所有元素
  • 判断功能
    boolean isEmpty ( ); //判断集合是否为空
    boolean contains( Object obj ); //判断集合是否包含指定的元素
    boolean containsAll( Collection c ); //判断集合中是否存在指定集合中的所有元素
  • 遍历集合
    Iterator iterator( ); //就是获取集合中的每个元素,别名迭代器。
  • 集合的使用步骤
    a:创建集合对象
    b:创建元素对象
    c:向集合中添加数据add()
    d:遍历集合
    • 通过集合对象调用iterator( )方法获取Iterator()接口的对象;
      注:当获取到集合的最后一个元素,他又继续获取元素就会报出异常NoSuchElementException。
    • 通过hasNext()方法进行判断指针后是否有元素存在;
    • 通过next()方法获取到指针,下一个元素;
    e:长度功能
    int size( ); //获取集合的长度
    f:交集功能
    boolean retainAll( Collection c ); //仅保留此 collection 中那些也包含在指定 collection中 的元素。
四、Collection接口的子接口List
  • list接口特点:
    a: 元素是有序的,元素可以重复,因为该集合体系有脚标索引。
    b: List接口的特有功能
    • 添加功能
      void add( int index,Object obj); //向集合指定位置添加元素
    • 删除功能
      Object remove( int index ); //删除集合中指定所引出的元素,返回被删除的元素值;删除后集合中的元素重新排列;
    • 修改功能
      Object set( int index ,Object obj ); //用指定的元素替换指定索引位置的值,返回被替换的元素的值。
    • 获取功能
      Object get( int index ); //获取集合指定索引处的值。
      int indexOf( Object obj ); //获取指定元素在集合中的第一次出现的索引。
      ListIterator listIterator( ); //是Literator的子接口,列表迭代器。由于在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会引起错误,所以在迭代时,只能使用迭代器的方法操作元素,可是Iterator方法是有限的(只有hasNext()、next()、remove()三个),所以引入ListIterator 接口中的方法获取其他操作。
    • 截取功能
      List subList( int formIndex, int toIndex); //获取集合从指定位置开始到指定位置结束,返回截取出来的集合。
  • 并发修改异常的产生和解决方案
    a:并发修改时怎样产生的呢?
    当我们通过迭代器迭代元素的过程中,又通过集合添加了元素。这种情况是不允许的。因为迭代器是依赖于集合存在的,如果集合发生了改变,迭代器也应该相应的发生改变。而现在迭代器没有改变,但集合发生了改变,所以报出了并发修改异常。
    b:解决方案
    注意问题:通过迭代器遍历集合的时候,是不能通过集合去操作(添加、删除)。
1、全部通过迭代器操作,使用列表迭代器 代码: ListIterator lit = list.listIterator(); while ( lit.hasNext()) { String s = (String) lit.next(); if ( "hello".equals( s)) { lit.add("IOS"); //全部通过迭代器操作:元素是添加到刚遍历的那个元素后面。 } } 2、全部通过集合操作: 代码: for ( int x = 0; x < list.size(); x++) { String s = (String) list.get( x); if ( "hello".equals( s)) { list.add("IOS"); //元素是添加到最后的。 } }

五、ArrayList概述
  • 定义:
    ArrayList是List接口的一个实现类,在它内部封装了一个长度可变的数组对象。其大部分的方法是从其父类中继承过来的。
  • ArrayList去重
    a:如果是字符串对象的话。只用创建新对象。通过iterator进行遍历。然后将获取的到字符串对象转型为字符,经过转型后,调用新集合的contains方法对获取到的元素进行判断,如果不存在就把元素存储在新集合中即可。
    b:如果是一个(学生)对象,该对象中有很多的属性和方法。如果采用的是contains方法进行比较。那么比较的时候默认比较的是对象的地址值。所以不管怎么比较它们是不可能相等的。这个时候需要看底层的代码。通过底层的代码可知,底层使用了equals方法。因此。我们可以对equals方法进行重写,让底层按照我们自己定义的规则来进行比较。所以,可以在学生类中添加equals方法。
    c: Equals方法中有比较有三部分:
    • 提高代码判断的效率: 采用 this == obj 的判断比较。这段代码用于判断传过来的对象是否是同一个对象,如果是同一对象的话,就 返回true。
    • 提高代码的健壮性: 采用 !(obj instance of Student) (以Stduent类来举例) 用于判断传过来的对象是否是Student 类的一个对象,如果不是,然后false
    • 比较对象的属性值是否相等:首相要比较该对象的属性值,就必须要把传过来的Object 类型的对象转化成为Student 对象的子类。这样才能调用student的属性。属于向下转型代码如下:
      Student stu = (Student) obj;
      分两种情况:
      1.比较字符串类型的成员变量: this.name.equals(stu.name) 注意这里的equals 是this.name 这个字符串类象的方法。并不是调用了本方法。
      2.比较整数类型的成员变量: this.age == stu.age; 比较两个整数类型的age是否相等。
      经过三次比较,基本上就穷尽了所有的点,这样就能实现去重的功能
六、Vector集合(Vector的特有功能)
  • 添加功能
    addElement(Object obj)
    add(Object obj)
  • 获取功能
    elementAt(int index)
    get(int index)
    elements()
    hasMoreElements()
    hasNext()
    nextElement()
    next()
  • 长度功能
    public int size()
七、LinkedList
  • 概述:
    LinkedList集合内部维护了一个双向循环链表,链表中的每个元素都使用引用的方式来记住它的前一个元素和后一个元素,从而可以将所有元素彼此连接起来。
  • 特有方法
    a:添加元素
    void addFirst(Object obj ); //向集合的开始位置添加元素。
    void addLast( Object obj )//向集合的末尾位置添加元素。
    b: 获取功能
    Object getFirst();//获取集合中的第一个元素
    Object getLast();//获取集合中最后一个元素
    c:删除功能
    Object removeFirst();//删除集合中第一个元素
    Object removeLast();//删除集合中最后一个元素
八、Collection接口的另一个子接口Set
  • Set简介
    Set接口继承自Collection接口,它与Collection接口中的方法基本一致,没有对Collection接口进行功能上扩充,但Set接口中的元素是无序的,并且都会以某种规则,并保证存入的元素不重复出现。
  • Set接口有两个实现类(HashSet和TreeSet)
    • HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能,线程不同步。
    • TreeSet则是以二叉树的方式来存储元素,它可以实现对集合中的元素进行排序,线程不同步。
  • HashSet集合
    • 当向HashSet集合中添加元素时,首先会调用该对象的hashCode()方法来确定元素的存储位置,然后再调用对象的equals()方法来确保该位置没有重复元素。
    • Set集合和List集合存取元素的方式一样,存入add()方法,获取采用迭代器或增强for循环。
    • 当向集合中存入自定义元素时,为了保证HashSet正常工作,要求在存入对象时,重写该类中的hashCode()和equals()方法。
复写: hashCode()方法 public int hashCode() final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }

复写:equals(Object obj)方法 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; }

  • TreeSet集合
    a、TreeSet集合中没有重复的元素,并且可以对元素进行排序,使用二叉树结构。
    b、那么,Treeset是如何存储数据的呢?
    • 把第一个存储进去的数据作为根节点
    • 把数据依次和根节点进行比较
    • 比较得到正数往右放
    • 比较得到负数向左放
    • 如果比较得到相等的数,把后来的数据扔掉
    • 从二叉树结构中取数据的内存规则(原则:从每个节点的左边开始,遵循原则 左 中 右)
    c、TreeSet保证元素排序有两种方式:
    • 元素的自然排序,或者叫默认顺序。让元素自身具备比较性, 元素所属的类需要去实现Comparable接口。并重写接口中的compareTo方法。
public int compareTo( Studetn s ) { //需求是比较年龄 int num = this.age - s.age ; //当某一个相同的时候,你还需要判断其他的是不是也是相同的。 int num2 = ( num == 0 ) ? ( this.name.compareTo( s.name ) ) : num ; return num2; }

  • 当元素自身不具备比较性,或者比较性不是所需要的,这时就需要让集合具备比较性。
    在集合初始化时,定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数)
    比较器接口 Comparator。
    带参构造。(匿名内部类),当两种排序都存在时,以比较器为主。
    定义一个MyComparator类实现Comparator接口,并在compare()方法中实现元素的比较,这就相当于定义了一个比较器。
a:定义一个比较器实现Compareator接口 class MyComparator implements Comparator{public int compare(Student s1,Student s2){ int num = s1.getName().length() -s2.getName().length(); int num1 = ( num == 0) ? (s1.getName().compareTo( s2.getName())) : num; int num2 = ( num1 == 0) ? ( s1.getAge() - s2.getAge()) : num1; return num2 ; }}

b:在内部类中实现比较器: TreeSet ts = new TreeSet( new Comparator() { @Override public int compare(Student s1, Student s2) { int num = s1.getName().length() - s2.getName().length(); int num1 = ( num == 0) ? (s1.getName().compareTo( s2.getName())): num; int num2 = ( num1 == 0) ? ( s1.getAge() - s2.getAge()) : num1; return num2; } } );

九、Map接口(双列集合的顶层接口)
  • Map接口概述
    a:Map接口是存储双列集合,键值成对出现,且一 一 对应,键的值必须唯一,值可以重复。
    b:Map双列集合体系图
    • HashMap集合:底层使用哈希表数据结构,允许使用null键和null值,线程不同步。
    • Hashtable集合:底层使用哈希表数据结构,不允许使用null键和null值,线程同步。
    • TreeMap 底层使用二叉树数据结构,线程不同步,可以用于给Map集合中的键进行排序,和Set很像,set底层就是使用了Map集合.
  • Map功能
    a:增加功能。
    V put(K key, V value)
    向集合中添加键值对
    如果该键存在,则返回被替换的值
    如果该键不存在则直接添加返回null
    b:删除功能。
    void clear()
    清空集合中所有数据
    V remove( Object key )
    根据键删除键和值这一对元素
    c:判断功能。
    boolean containsKey( Object key )
    判断集合中是否包含指定的键
    boolean containsValue( Object value )
    判断集合中是否包含指定的值
    boolean isEmpty()
    判断集合是否为空
    d:获取功能。
    Set> entrySet()
    获取键值对儿,对象的集合
    Object get( Object key )
    根据键获取值
    Set keySet():
    获取集合中所有键的Set集合
    Collection values()
    获取集合中所有值的集合
    e:长度功能。
    int size()
    获取集合的长度
  • Map集合中元素的两种取出方式
    a: Set keySet()方法,将map集合中所有的键存入到set集合中,因为set集合具有迭代器,所以可以用迭代的方法,取出所有的键(增强for(){ }循环),再根据 Object get(Object key)方法,获取每个键所对应的值,
    b:Set> entrySet() 方法,将map集合中的映射关系存入到set集合中,而这个关系的数据类型就是Map.Entry,然后使用增强for(){ }循环遍历,再使用Map.Entry接口的(getKey()和getValue()方法)获取键和值。
  • HashMap和TreeMap集合
    a:HashMap集合存储自定义对象做键,要重写hashCode()方法和equals()方法,因为如果用自定义对象当做键的话,要保持元素的唯一性(因为键是唯一的)。
    b:TreeMap集合存储自定义对象做键,要做一下操作:
    实现排序方式一:让对象所属的类去实现Comparable接口
    实现排序方式二:用TreeMap的带参构造,在参数位置接收 Comparator 接口对象.
    //内部类,自定义比较器
  • 【4、单双列集合】HashMap和Hashtable的区别
    a:HashMap 线程不安全,效率高.允许键和值为null.
    b:Hashtable线程安全,效率低,不允许键和值为null。

    推荐阅读