全国咨询热线:400-618-9090

首页技术文章正文

集合的总结与常见问题解析

创建时间:2018-11-26 17:29:53.0 来源:黑马程序员

  1、如何定义Collection?

  Collection是一个集合体系的顶层接口,此接口中的方法都是:public abstract。

  可以进行增删改查的操作:

  增加: -->> boolean add(Object o) 一次添加一个元素,元素可以是Object 的子类,返回true

  boolean addAll(Collection c) 一次添加一批元素,参数是Collection类型的

  删除: -->> void clear() 清空集合

  boolean remove(Object o) 一次删除一个元素,成功删除,返回true

  boolean removeAll(Collection c) 一次删除一批,成功,返回true

  查找: -->> boolean contains(Obejct o) 判断集合中是否包含指定元素

  boolean containsAll(Collection c) 是否包含一批

  boolean isEmpty() 判断集合是否为空

  int size() 获取集合中的个数

  修改: -->> clear() 清空集合中的所有元素

  获取: -->> int size() 返回集合容器的大小

  集合转换为数组:

  Object[] toArray(); 该方法将集合转为数组,集合中的每一个元素,作为了数组一个的元素

  【注意:集合中存储的是对象的引用,而不是对象本身】

  【注意:接口与接口之间属于:继承关系】

  2、录入用户在控制台输入的信息,可以使用Scanner类

  Scanner sc = new Scanner();

  3、List接口: ---->> public interface List extends Collection

  List是一个继承了Collection接口的接口,具备了比Collection多的功能。

  特点: ---->> 有序、有角标、可重复。

  此接口的用户可以对List接口中每个元素的插入位置进行精确地控制。(通过角标获取集合中的元素)

  增:

  void add(int index,Object element) 指定元素添加的角标,增加一个元素

  boolean addAll(int index,Collection c) 指定位置,增加一批

  删:

  Element remove(int index) 删除指定位置的元素,并且返回该元素

  改:

  Element set(int index,Object element) 替换指定位置的元素,需要确定该元素的位置和新元素,返回就元素。

  ListsubList(int fromIndex,int toIndex) 截取子结合,返回一个List,包含头,不包含尾。

  查:

  Object get(int index) 获取List结合指定位置的元素

  int indexOf(Object o) 返回此列表中第一次出现的指定元素的索引

  int lastIndexOf(Object o) 倒序查找指定元素的位置

  【注意角标越界:IndexOutOfBoundException // 找不到返回:-1】

  【Object set = new list.get(list.size()-1);】

  【List集合特有的增删改查方法都跟角标有关系!!!!】

  【问:如何取出List集合中的元素?】

  通过for循环,并使用size()和set()方法可取出List集合中的每一个元素。

  4、ArrayList -->> 是一个List接口的实现类

  具备List集合的特点:有序、有角标、可重复。

  该类内部,维护了一个数组, 数组的元素是Object 类.

  在ArrayList 类的构造函数中初始化的该数组, 如果没有显示的指定数组的长度,【默认长度是10】

  也就是说: 使用无参数构造,new ArraList(), 自动创建的数组长度是60%.

  ArrayList 在添加元素时,都会检测数组是否已满,如果满了,自动扩容(创建一个新数组),

  【新数组长度的是老数组的1.6倍】

  并且将老数组中的元素拷贝到了新数组,使用新数组继续增加元素.

  5、ArrayList使用什么实现的?其优缺点是什么?

  ArrayList是使用数组实现的。

  优点:内存地址连续,查找快;

  缺点:增加和删除元素,需要设计数组的扩容或者拷贝,效率很低。

  ---->> 总结: 数组实现,查找快,增删慢。

  ArrayList中的方法:

  boolean contains(Object o) 判断集合是否包含指定的元素,涉及了元素的比较(对象的比较)

  内部使用了元素的equals()方法。

  String类 重写了equals()方法

  6、LinkedList ---->> List接口的实现类,具备:有序、有角标、元素可重复的特点。【和ArrayList类似】

  LinkedList是一个双向链表,该集合提供了方便操作集合头和集合尾的方法。

  【如果集合中没有元素可以获取或者删除,则抛:NoSuchElementException】

  LinkedList特有的方法:

  增加:

  void addFirst(Element e)

  void addLast(Element e)

  删除:

  Object getFirst()

  Object getLast()

  查找:

  Object removeFirst() 移除并返回此列表的第一个元素

  Object removeLast() 移除并返回此列表的最后一个元素

  数据结构:

  堆栈:

  void push(Element e) 将元素推入此列表所表示的堆栈

  Element pop() 从此列表所表示的堆栈处弹出一个元素

  【压栈和弹栈:先进后出,后进先出】

  队列:

  boolean offer(Element e) 将指定元素添加到此列表的末尾(最后一个元素)

  Element poll() 获取并移除此列表的头(第一个元素)

  Element peek() 获取但不移除列表的头(第一个元素)

  【队列的数据结构:先进先出】

  返回逆序的迭代器对象:

  descendingIterator() 返回逆序的迭代器对象

  7、LinkedList的实现原理是什么?其优缺点?

  LinkedList的实现原理是:链表实现,其内存地址是不连续的。

  优点:相对于数组,增加元素快;

  缺点;由于内存地址不连续,查找性能低。

  8、集合与数组有什么区别?

  相同点:数组和集合都是容器

  数组和集合中存放的都是【对象的引用】而非对象本身

  不同点:数组存储【基本数据类型】,是单一的。而且一旦声明好长度后,长度不可变;

  集合【只能】储存【对象】,但是可以是任意类型的对象,其长度可变。

  【集合的分类】

  ----|Iterable:接口

  Iterator iterator()

  ----|Collection:单列集合

  ----|List: 有序存储顺序,可重复

  ----|ArrayList: 数组实现,查找快、增删慢

  由于是数组实现,在增和删的时候会牵扯到数组增容,以及拷贝元素,所以慢;

  数组是可以直接按索引查找的,所以查找时比较快。

  ----|LinkedList: 链表实现,增删快、查找慢

  由于链表实现,增加时只要让前一个元素记住自己就可以了,删除时让前一个元

  素记住后一个元素,后一个元素记住前一个元素,这样的增删效率高;

  但查询时需要一个一个的遍历,所以效率比较低。

  ----|Vector: 多线程安全、效率略低 【ArrayList单线程效率高,但是多线程要使用Vector】

  ----|set: 无序存储,不可重复

  ----|HashSet 线程不安全,存取速度快

  底层是以hash表实现的

  ----|TreeSet 红-黑树的数据结构,默认对元素进行自然排序(String)

  【TreeSet自身具备排序功能】

  ----|Comparable

  ----|compareTo(Object o) 元素自身具备比较性

  ----|Comparator

  ----|compare(Object o1,Object o2) 给容器传入比较器

  如果在比较的时候两个对象返回值是【0】,那么这两个元素【重复】

  【当Comparable和Comparator比较方式同时存在时,以Comparator比较方式为主】

  ----|LinkedHashSet 会保存插入的顺序

  ----|Map: 将键映射到值的对象。一个映射不能包含重复的键,每个键最多只能映射一个值。

  interface Map

  ----|TreeMap 底层是二叉树数据结构,可以对map集合中的键进行排序

  需要使用Comparable或者Comparator进行比较排序。

  【return 0 判断键的唯一性】

  ----|HashTable 底层是哈希表数据结构,线程是【同步】的 -->> 不可以存入null键、null值

  效率较低,故被【HashMap】替代

  ----|HashMap 采用哈希表实现 -->> 【无序】

  底层是哈希表数据结构,线程是【不同步】的 -->> 可以存入null键、null值

  【要保证键的唯一性,需要覆盖hashCode()方法和equals()方法】

  ----|LinkedHashMap

  【常用方法:】

  添加: V put(K key,V value) 可以是相同的key值,但是添加的value值会覆盖前面的

  putAll(Map m)

  从指定映射中将所有映射关系复制到此映射中(可选操作)

  删除: remove(Object key) 删除关联对象,指定key对象

  clear() 清空集合对象

  获取: value get(Object key) 可以用于判断键是否存在的情况。

  判断: boolean isEmpty() 如果此映射不包含键-值映射关系【即长度为0】,则返回true,否则返回false

  boolean containsKey(Object key)

  判断集合中是否包含指定的key

  boolean containsValue(Object value)

  判断集合中是否包含指定的value

  当指定的键不存在的时候,返回的是null

  长度: int size() 返回此映射中的键-值映射关系数

  9、在什么时候该使用什么样的集合?

  Collection 当我们需要保存若干个对象的时候使用集合

  -->> List 如果需要保留存储顺序、并且重复元素时,使用List

  -->> 如果查询较多,使用ArrayList;

  如果存取较多,使用LinkedList;

  如果需要线程安全,使用Vector。

  -->> Set 如果不需要保留存储顺序,并且要去掉重复元素时,使用Set

  -->> 如果需要将元素排序,使用TreeSet;

  如果不需要排序,使用HashSet 【HashSet比TreeSet效率高】

  如果需要保留存储顺序,同时要过滤重复元素,使用LinkedHashSet。

  10、自定义对象时为什么要重写toString()和equals()方法?

  因为Object是自定义类的父类,Object类中的toString()方法返回的是哈希值;

  Object类中的equals()方法比较的是对象的地址值。

  【去除集合中重复的元素】

  代码如下:

  public class Demo{

  public static void main(String[] args){

  ArrayList arr = new ArrayList();

  Person p1 = new Person("jack",20);

  Person p2 = new Person("rose",18);

  Person p3 = new Person("rose",18);

  arr.add(p1);

  arr.add(p2);

  arr.add(p3);

  System.out.println(arr);

  ArrayList arr2 = new ArrayList();

  for(int i=0;i<arr.size();i++){< p="">

  Object obj = arr.get(i);

  Person p = (Person)obj;

  if(!arr2.contains(p)){

  arr2.add(p);

  }

  }

  System.out.println(arr2);

  }

  }

  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;

  }

  public int hashcode(){

  return this.name.hashCode() + age*37;

  }

  public boolean equals(Object obj){

  if(!(obj instanceof Person)){

  return false;

  }

  Person p = (Person)obj;

  return this.name.equals(p.name) && this.age = p.age;

  }

  public String toString(){

  return "name:" + this.name + "age:" + this.age;

  }

  }

  11、Vector: 多线程安全、但是效率低 ---->> 描述的是一个线程安全的ArrayList。

  特有的方法:

  void addElement(E obj) 在集合末尾添加元素

  E elementAt(int index) 返回指定角标的元素

  Enumeration element() 返回集合中的所有元素,封装到Enumeration对象中

  Enumeration接口:

  boolean hasMoreElements() 测试此枚举是否包含更多的元素

  E nextElement() 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素

  【代码如下:】

  public static void main(String[] args){

  Vector v = new Vector();

  v.addElement("aaa");

  v.addElement("bbb");

  v.addElement("ccc");

  // System.out.println(v.elementAt(2));

  Enumeration ens = v.elements();

  while(ens.hasMorreElements()){

  System.out.println(ens.nextElement());

  }

  }

  12、Iterable: 是Collection的父接口,实现Iterable的类可以进行迭代,并且支持增强for循环

  该接口只有一个方法,用于返回集合迭代器对象! 【获取迭代器的方法iterator()】

  piblic interface Iterable

  Iteratoriterator():该类主要用于遍历集合对象,并描述了遍历集合的常见方法

  boolean hasNext() 判断集合中是否有元素,如果有元素可以迭代,就返回true。

  E next() 返回迭代的下一个元素。

  如果没有下一个元素,调用next()会抛出 -->> NoSuchElementException

  void remove() 从迭代器指向的集合中移除迭代器返回的最后一个元素

  【Iterator的for循环、清空】

  public class Demo{

  ArrayList list = new ArrayList();

  // 增加:add() 将指定对象存储到容器中

  list.add("计算机网络");

  list.add("现代操作系统");

  list.add("java编程思想");

  list.add("java核心技术");

  list.add("java语言程序设计");

  System.out.println(list);

  for(Iterator it = list.iterator();it.hasNext();){

  // 迭代器的next()方法返回值类型是Object,所有要记得【类型强转】

  String next = (String)it.next();

  System.out.println(next);

  it.remove();

  }

  }

  【细节一:如果迭代器的指针已经指向了集合的末尾,那么如果再调用next()会返回NoSuchElementException异常】

  【细节二:如果调用remove()之前没有调用next()方法是不合法的,会抛出IllegalStateException异常】

  【细节三:当一个集合在循环中即使用引用变量操作集合,又使用迭代器操作集合对象,会抛出ConcurrentModificationException异常】

  13、为什么next()方法的返回值类型是Object呢?

  为了可以接收任意类型的对象

  如果返回的时候不知道是什么类型的,就定义为object

  14、Iterator和ListIterator有什么关系?

  ListIterator是Iterator的子接口,是List集合特有的迭代器。

  Iterator在迭代时,只能对元素进行获取【next()】和删除【remove()】的操作;

  ListIterator在迭代list集合时,还可以对元素进行添加【add(obj)】和修改【set(obj)】的操作。

  15、List集合特有的迭代器ListIterator

  ---->> public interface ListIterator extends Iterator

  ListIteratorlistIteraotr()

  ----| Iterator

  hasNext()

  next()

  remove()

  ----| ListIterator

  add(E e) 将指定的元素插入列表(可选操作)。

  该元素直接插入到next()返回的下一个元素的前面(如果有)

  void set(E o) 用指定的元素替换next()或previous()返回的 【最后】 一个元素

  hasPrevious() 逆向遍历列表,列表迭代器有多个元素,则返回true

  previous() 返回列表中的前一个元素

  16、HashSet是如何判断两个元素重复的?

  通过hashCode()方法和equals()方法来保证元素的唯一性,add()方法返回的是boolean类型

  【调用原理:HashSet集合在判断元素是否相同,先判断hashCode()方法,相同才会判断equals()方法;不相同不会调用equals()】

  17、HashSet和ArrayList集合在判断元素时是否有相同的方法?

  有:boolean contains(Object o)

  HashSet使用hashCode()和equals()方法,ArrayList使用eqauls()方法。

  18、给TreeSet指定排序规则:

  方式一:【元素自身】具备比较性

  元素自身具备比较性,需要元素实现【Comparable接口】,重写【compareTo方法】,

  也就是让元素自身具备比较性,这种方式叫做元素的【自然排序】也叫做【默认排序】。

  方式二:【容器】具备比较性

  当元素自身不具备比较性,或者自身具备的比较性不是所需要的。

  那么此时可以让容器自身具备。需要定义一个类实现【Comparator接口】,重写【compare方法】,

  并将该接口的子类实例对象作为参数传递给【TreeSet集合】的【构造方法】。

  注意:当Comparable比较方式和Comparator比较方式同时存在时,以【Comparator】的比较方式为主;

  注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。

  通过return 0 来判断唯一性。

  19、为什么使用TreeSet存入字符串,字符串默认输出是按升序排列的?

  因为字符串实现了一个接口,叫做【Comparable接口】,字符串重写了该接口的【compareTo()方法】,

  所以String对象具备了比较性。

  【自定义的元素(比如Person类、Book类)想要存入TreeSet集合,就必须实现Comparable接口,也就是要让自定义对象具备比较性】

  【存入TreeSet集合的元素都要具备比较性:要实现Comparable接口、并重写该接口的compareTo()方法】

  20、总结:

  看到array,就要想到角标。

  看到link, 就要想到first,last。

  看到hash, 就要想到hashCode,equals.

  看到tree, 就要想到两个接口。Comparable,Comparator。

  21、TreeSet是如何保证元素的唯一性的?

  通过【compareTo】或者【compare】方法来保证元素的唯一性。

  当Comparable接口中的compareTo()函数返回值为【0】时,说明两个对象相等,此时该对象不会被添加进来。

  22、使用TreeSet集合将字符串 String str = "8 10 15 5 2 7"; 的数值进行排序。

  public class Demo{

  public static void main(String[] args){

  String str = "8 10 15 5 2 7";

  String strs = str.split(" ");

  TreeSet ts = new TreeSet();

  for(int x= 0;x<strs.length();x++){< p="">

  int y = Integer.parseInt(strs[x]);

  ts.add(y);

  }

  System.out.println(ts);

  }

  }

  23、遍历Map集合的方式有哪些?

  方式一:使用keySet

  将Map转成Set集合【keySet()】,通过Set的迭代器【Iterator】取出Set集合中的每一个元素,

  即Map集合中所有的键,再通过get()方法获取键对应的值

  Setks = map.keySet();

  Iteratorit = ks.iterator();

  while(it.hasNext()){

  Integer key = it.next();

  String value = map.get(key);

  }

  方式二:通过values获取所有值,但是不能获取到key对象

  Collectionvs = map.values();

  Iteratorit = vs.iterator();

  while(it.hasNext()){

  String value = it.next();

  }

  方式三:Map.Entry -->> public static interface Map.Entry

  通过Map中的entrySet()方法获取存放Map.Entry对象的Set集合 -->> Set<map.entry> entrySet()

  Set<map.entry> entrySet = map.entrySet();

  Iterator<map.entry> it = entrySet.iterator();

  while(it.hasNext()){

  Map.Entryen = it.next();

  }

  Integer key = en.getKey();

  String value = en.getValue();



作者:黑马程序员JavaEE培训学院

首发: http://java.itheima.com

在线咨询 我要报名