Comparable ,Collection,Set, List, Map, Runnable Iterable Iterator
Comparable接口
Collections类中包含很多对实现Collection接口的容器各种操作的静态方法.
当然, 其中最长用的莫过于排序了(Collections.sort(List l).
例:
public class Compare1{
public static void f(){
ArrayList arr = new ArrayList();
arr.add(10);
arr.add(23);
arr.add(7);
System.out.println(arr);
Collections.sort(arr);
System.out.println(arr);
}
}
逻辑很简单, 就是在1个list容器中添加3个int数值(注意实际被自动装箱成Interger对象).
正常输出容器元素一次, 利用Collections.sort()方法排序后, 再输出1次.
输出:
[10, 23, 7]
[7, 10, 23]
但是当List容器添加的元素对象是属于自己写的类时, 就可能出问题了.
例:
import java.util.ArrayList;
import java.util.Collections;
class Student{
private String name;
private int ranking;
public Student(String name, int ranking){
this.name = name;
this.ranking = ranking;
}
public String toString(){
return this.name + “:” + this.ranking;
}
}
public class Compare2{
public static void f(){
ArrayList arr = new ArrayList();
arr.add(new Student(“Jack”,10));
arr.add(new Student(“Bill”,23));
arr.add(new Student(“Rudy”,7));
System.out.println(arr);
}
}
上面定义了1个Student类, 它只有两个成员, 名字和排名.
在f()方法内, 添加3个Student的对象到1个list容器中, 然后输出(必须重写String方法, 这里不解释了):
[Jack:10, Bill:23, Rudy:7]
到此为止, 是没有问题的. 但是当我对这个容器进行排序时就有问题了.
例如将上面的f()方法改成:
public class Compare2{
public static void f(){
ArrayList arr = new ArrayList();
arr.add(new Student(“Jack”,10));
arr.add(new Student(“Bill”,23));
arr.add(new Student(“Rudy”,7));
System.out.println(arr);
Collections.sort(arr);
System.out.println(arr);
}
}
编译时就会出错:
[java] Caused by: java.lang.ClassCastException: Collection_kng.Comparable_kng.Student cannot be cast to java.lang.Comparable
提示这个类Student没有实现Comparable接口.
原因也很简单, 因为Java不知道应该怎样为Student对象排序, 是应该按名字排序? 还是按ranking来排序?
为什么第1个例子就排序成功? 是因为Java本身提供的类Integer已经实现了Comparable接口. 也表明Integer这个类的对象是可以比较的.
而Student类的对象默认是不可以比较的. 除非它实现了Comparable接口.
总而言之, 如果你想1个类的对象支持比较(排序), 就必须实现Comparable接口.
Comparable 接口内部只有1个要重写的关键的方法.
就是
int compareTo(T o)
这个方法返回1个Int数值,
例如 i = x.compareTo(y)
如果i=0, 也表明对象x与y排位上是相等的(并非意味x.equals(y) = true, 但是jdk api上强烈建议这样处理)
如果返回数值i>0 则意味者, x > y啦,
反之若i<0则 意味x < y
Comparable接口的实现及用法.
用回上面的例子,我们修改Student类, 令其实现Comparable接口并重写compareTo方法.
import java.util.ArrayList;
import java.util.Collections;
class Student implements Comparable{
private String name;
private int ranking;
public Student(String name, int ranking){
this.name = name;
this.ranking = ranking;
}
public String toString(){
return this.name + “:” + this.ranking;
}
public int compareTo(Object o){
Student s = (Student)(o);
return this.ranking - s.ranking;
}
}
public class Compare2{
public static void f(){
ArrayList arr = new ArrayList();
arr.add(new Student(“Jack”,10));
arr.add(new Student(“Bill”,23));
arr.add(new Student(“Rudy”,7));
System.out.println(arr);
Collections.sort(arr);
System.out.println(arr);
}
}
注意重写的compareTo(Object o)方法内. 根据Student的ranking成员来比较的, 也就是说跟姓名无关了.
这时再编译执行, 就能见到List容器内的Student对象已经根据ranking来排序了.
输出:
[Jack:10, Bill:23, Rudy:7]
[Rudy:7, Jack:10, Bill:23]
————————————————————————————————————
Collection 下常用子接口有List 和Set
实现了List 的常用类有ArrayList 和LinkedList
实现了Set 的常用类有HashSet 和TreeSet
list接口
有序的 集合,此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
list与Set不同,list允许重复的元素插入。
ArrayList和LinkedList的大致区别如下:
LinkedList和ArrayList的差别主要来自于Array和LinkedList数据结构的不同。如果你很熟悉Array和LinkedList,你很容易得出下面的结论:
1) 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
2) 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
3) 类似于插入数据,删除数据时,LinkedList也优于ArrayList。
4) LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
因此,ArrayList更适合读取数据,linkedList更多的时候添加或删除数据。
Set接口
无序的集合,最多支持一个null值,不允许重复元素,没有元素索引。set中的常用方法与collection中的一致,上面已经写过。
HashSet和TreeSet异同:
相同点:
单列集合,元素不可重复
不同点
1. 底层存储的数据结构不同
HashSet底层用的是HashMap哈希表结构存储,而TreeSet底层用的是TreeMap树结构存储
2.存储时保证数据唯一性依据不同
HashSet是通过复写hashCode()方法和equals()方法来保证的,而HashSet通过Compareable接口的compareTo()方法来保证的
3.有序性不一样
HashSet无序,TreeSet有序
HashSet
内部数据结构是哈希表 ,不支持同步(线程不安全),基于HashMap实现
hashSet存储元素的原理:
1.首先通过hashCode()方法确定元素位置
2.再次通过equals()方法确定元素内容是否重复
所以,在使用的过程中,有必要的情况下,要重写hashCode()方法和equals()方法
注意:该接口下的实现类的contains()方法是hashCode()和equals()两个方法,remove()方法也是一样
LinkedHashSet,继承于HashSet,它与HashSet是不同的,相比于HashSet,它是有序的,因为LinkedHashSet的内部数据结构是一个双向链表,此链表定义了迭代顺序,即按照Set的插入顺序进行迭代,使用它可以生成一个Set副本,并且与原Set无关。
例:
public class LinkedHashSetTest{
public static void main(String[] args){
LinkedHashSet books = new LinkedHashSet();
books.add(“世界您好”);
books.add(“您好世界”);
System.out.println(books);
books.remove(“世界您好”); //删除世界您好
books.add(“世界您好”); //重新添加世界您好
System.out.println(books);
}
}
输出:
[世界您好, 您好世界]
[您好世界, 世界您好]
TreeSet
基于TreeMap实现,内部结构是二叉树,不支持同步,支持对元素的自然排序或者根据创建Set时的提供comparator进行排序
TreeSet判断元素唯一性,是根据比较方法中的返回值:0==等于 1==大于 -1==小于
TreeSet集合对元素进行排序的方式
1.元素的自然排序,让元素自身具有比较功能,元素实现comparable接口,覆盖compareTo()方法,这种方式,是对元素的自然排序,这是Java中一种默认排序方式
2.集合的自然排序,让集合自身具有比较功能,创建类实现comparator接口,覆盖compare()方法,然后将该对象传递给创建TreeSet的构造函数;
该方式的使用场景:对象中不具备自然排序的方式,对象中的自然排序方式不适合的时候
Iterator接口:
使用Iterator接口遍历集合元素,Iterator接口是JAVA集合框架的成员,也被称为迭代器。
Iterator接口中声明了如下方法:
boolean hashNext():如果迭代的集合元素还没被遍历,则返回true
Object next():返回集合里下一个元素。
Void remove():删除集合里上一次next方法返回的元素。
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class TextIterator {
public static void main(String[] args){
Collection books=new HashSet(); //无序序列,元素不可重复
books.add(“世界你好”);
books.add(“你好世界”);
books.add(“你好Java”);
System.out.println(“现在结合的元素:”+books);
Iterator it=books.iterator(); //获取books集合对应的迭代器。
while(it.hasNext()){
String book=(String)it.next(); //it.next()方法返回的数据类型是object类型,需要强制类型转换。
System.out.println(book);
if(book.equals(“你好世界”)){
it.remove(); //从集合中删除上一次next方法返回的元素。
}
book=”测试字符串”; //对book变量赋值,不会改变集合元素本身。
}
System.out.println(books);
}
输出:
现在结合的元素:[世界你好, 你好Java, 你好世界]
世界你好
你好Java
你好世界
[世界你好, 你好Java]
结论:Iterator仅用于遍历集合,Iterator本身并不提供盛装对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的集合。当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素本身没有任何改变。