集合深入研究(1)



从图上看出容器分为三大类:List,Map,Set.

collection:父接口;
Set:接口 ---一个实现类: HashSet
List:接口---三个实现类: LinkedList,Vector,ArrayList
SortedSet:接口---实现类:TreeSet.


1、List:
List:有序列表,允许存放重复的元素;
     正如你所看到的大多数时候只是调用add()添加对象,使用get()一次取出一个元素,以及调用iterator()获取用于改序列的Iterator.
实现类:
ArrayList:数组实现,查询快,增删慢,线程不安全,轻量级;下标也是从0开始;
LinkedList:链表实现,增删快,查询慢
Vector:数组实现,线程安全,重量级(过时)




2.Set:
无序集合,不允许存放重复的元素;
实现类 HashSet:equals返回true,hashCode返回相同的整数;哈希表;
子接口SortedSet:对Set排序 实现类 :TreeSet:二叉树实现的;


3.Map
HashMap:键值对,key不能重复,但是value可以重复;key的实现就是HashSet;value对应着放;
HashSet 的后台有一个HashMap;初始化后台容量;只不过生成一个HashSet的话,系统只提供key的访问;
如果有两个Key重复,那么会覆盖之前的;
 
Hashtable:线程安全的
Properties:java.util.Properties; key和value都是String类型,用来读配置文件;
 
HashMap与Hashtable区别:
HashMap线程不安全的,允许null作为key或value;
Hashtable线程安全的,不允许null作为key或value;
 
TreeMap: 对key排好序的Map; key 就是TreeSet, value对应每个key;
key要实现Comparable接口或TreeMap有自己的构造器;


两个工具类 Arrays 和 Collections
1.Arrays、此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂
2.Collections、主要提供了在 collection 上进行操作的静态方法




队列:
     队列在java中有两个实现:LinkedList和PriorityQueue,他们的差异在于排序行为而不是性能。




集合和数组的比较:


1:如果是性能考虑数组优先。
   而且集合的底层都是数组实现的。


2:警惕数组的浅拷贝。
   public class Client {
public static void main(String[] args) {
//气球数量
int ballonNum = 7;
// 第一个箱子
Balloon[] box1 = new Balloon[ballonNum];
// 初始化第一个箱子中的气球
for (int i = 0; i < ballonNum; i++) {
box1[i] = new Balloon(Color.values()[i], i);
}
// 第二个箱子的气球是通过拷贝第一个箱子的
Balloon[] box2 = Arrays.copyOf(box1, box1.length);
// 修改最后一个气球颜色
box2[6].setColor(Color.Blue);
// 打印出第一个箱子中气球颜色
for (Balloon b:box1) {
System.out.println(b);
}


}
}
//气球颜色
enum Color {
Red, Orange, Yellow, Green, Indigo, Blue, Violet;
}


//气球
class Balloon {
// 编号
private int id;
// 颜色
private Color color;


public Balloon(Color _color, int _id) {
color = _color;
id = _id;
}


public Color getColor() {
return color;
}


public void setColor(Color color) {
this.color = color;
}


public int getId() {
return id;
}


public void setId(int id) {
this.id = id;
}

public String toString() {
return "编号:"+id+"颜色:"+color.toString();
}


}


打印出来:
编号:0颜色:Red
编号:1颜色:Orange
编号:2颜色:Yellow
编号:3颜色:Green
编号:4颜色:Indigo
编号:5颜色:Blue
编号:6颜色:Blue


通过copyOf()方法产生的数组是个浅拷贝,这与序列化的浅拷贝完全相同,基本类型是直接拷贝,其他的都是引用拷贝。需要说明的是数组的
clone()方法也与此相同,同样是浅拷贝,而集合的clone()也是浅拷贝,这就需要大家再拷贝时多留心。




3:原始类型数组不能作为asList的输入参数,否则会引起逻辑混乱.


4:asList的方法产生的List对象不可改变。


5:不同的列表选择不同的遍历方法
  (1)arrayList采用下标方式遍历效率高。
  (2)LinkedList采用foreach遍历效率高。
 因为arrayList实现了RandomAccess接口,表明arrayList是个可以随机存储的列表。而java中的foreach是iterator(迭代器)的变形
 用法。那什么事迭代器?迭代器提供了一种方法访问一个容器对象中的各个元素,同时又无需暴露该对象内部细节,也就是说对于
 arrayList需要首先创建一个迭代器容器,然后屏蔽内部遍历细节,对外提供hasNext,next等方法。问题是arrayList实现了RandomAccess
 接口,已表明元素之间本没关系,可是为了使用迭代器就需要强制建立一种互相“知晓”的关系,比如上一个元素可判断是否有下一个元素
 ,以及下一个元素师什么关系,这也就是为什么arrayList使用foreach遍历耗时而LinkedList却步耗时的原因。


5:判断集合是否相等只需关心元素是否相等。


6:subList产生的列表只是一个视图,所以的修改动作直接作用于原列表。
  public static void main(String[] args) {
//定义一个包含两个字符串的列表
List<String> c = new ArrayList<String>();
c.add("A");
c.add("B");
//构造一个包含c的字符串列表
List<String> c1 = new ArrayList<String>(c);
//通过subList生成与c相同的列表
List<String> c2 = c.subList(0, c.size());
//c2增加一个元素
c2.add("C");
System.out.println("c == c1? " + c.equals(c1));
System.out.println("c == c2? " + c.equals(c2));
}
输出结果:c == c1? false
          c == c2? true
为什么修改用subList产生的列表会改变原列表?那我们看一下subList源码的实现。
class SubList<E> extends AbstractList<E>{
   //原始列表
   private AbstractList<E> l;
   //偏移量
   private int offset;
   //注意list参数就是我们的原始列表
   SubList(AbstractList<E> list, int formIndex, int toIndex){
        //传递原始列表
         l = list;
         offset = formIndex;
        //子列表的长度
         size = toIndex - formIndex;
    }


    //增加或者插入
     public void add(int index, E element){
       //直接增加到原始字符串上
        l.add(index+offset,element);
     }
 
    //获取指定位置的元素
    public E get(int index){
      //从原始字符串中获取指定位置元素
       return l.get(index+offset);
    }
 }


7:生成子列表之后不要再操作原列表,否则会抛出异常。


8:Comparable接口可以作为实现类的默认排序法,Comparator接口则是一个类的扩展排序法。


9:不推荐使用binarySearch对列表进行检索.
   binarySearch方法是对一个列表进行检索的,可查找指定值的索引值,但使用时有一些注意事项。
   public static void main(String[] args) {
List<String> cities = new ArrayList<String>();
cities.add("上海");
cities.add("广州");
cities.add("广州");
cities.add("北京");
cities.add("天津");
//indexOf方法取得索引值
int index1 = cities.indexOf("广州");
//binarySearch查找到索引值
int index2 = Collections.binarySearch(cities, "广州");
System.out.println("索引值(indexOf):"+index1);
System.out.println("索引值(binarySearch):"+index2);
}
  打印值:
         索引值(indexOf):1
         索引值(binarySearch):2
 为什么会产生不一样的结果?
 原因在于binarySearch在查找的时候使用的是二分查找法,问题就出现在二分查找法上。
 看一下源码:
 public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key){
     if(list instanceof RandomAccess || list.size()<5000){
          return Collections.indexedBinarySearch(list,key);
        }
     else{
         return Collections.iteratorBinarySearch(list,key);
         }
  }
 ArrayList实现了RandomAccess接口,是一个顺序存储列表,使用了indexedBinarySearch方法
  int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key)
    {
       //默认上界
int low = 0;
       //默认下界
int high = list.size()-1;


while (low <= high) {
          //中间索引,无符号右移一位
   int mid = (low + high) >>> 1;
          //中间值
   Comparable<? super T> midVal = list.get(mid);
          //比较中间值
   int cmp = midVal.compareTo(key);
          //重置上界和下界
   if (cmp < 0)
low = mid + 1;
   else if (cmp > 0)
high = mid - 1;
   else
            //找到元素
return mid; // key found
}
return -(low + 1);  // key not found
    }


我们再来看一下indexOf的源码:
   public synchronized int indexOf(Object o, int index) {
if (o == null) {
   for (int i = index ; i < elementCount ; i++)
if (elementData[i]==null)
   return i;
} else {
   for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
   return i;
}
return -1;
    }
可以从代码中看到只要找到就返回该值。既然知道了两种方法的原理就知道为什么一个返回1,一个返回2了。
所以如果要使用binarySearch方法,就必须给数据集实现升序排列。但这样真的可以吗?如果是一个有规则的业务数据
,我们要对他排列就改变了原来元素在列表中的位置,所以说用binarySearch在此受限制了。




10:集合中的元素必须做到compareTo和equals的同步。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值