重温《JAVA编程思想》----2017.1.23+24 容器/集合

前言:关于容器,http://blog.csdn.net/chenssy/article/details/37909815这个博客的集合系列讲的非常好,本篇博客只包含一些自己感兴趣的。

 

 

 

 

1.作用有限的方法:

Collection.nCopies()返回List

Collection.fill()只对List对象有用,并且只能替换已经存在的元素,而不能添加新的元素。

 

2.使用Abstract类:

每个java.util的容器都有其自己的Abstract:

A.)为了创建只读的Map,可以继承AbstractMap并实现entrySet()方法;

B.)为了创建只读的set,可以集成AbstractSet并实现Iterator()size();

C.)为了创建只读的List,必须实现get()size()

 

 

 

class MyMapextends AbstractMap<String, String>{

private class Entryimplements Map.Entry<String, String>{

 

@Override

public String getKey() {

return null;

}

 

@Override

public String getValue() {

return null;

}

 

@Override

public String setValue(String value) {

return null;

}

}

 

private class MySet extends AbstractSet<Entry>{

 

@Override

public Iterator<Entry> iterator() {

return null;

}

 

@Override

public int size() {

return 0;

}

}

@Override

public Set<java.util.Map.Entry<String, String>> entrySet() {

return null;

}

}

 

 

如上,可以发现Map里面有内部类Entry((它只存储索引,而不是keyValue)实现Map.Entry,必须实现方法getkey()getValue()setValue(),注意!没有setKey()),内部类EntrySet(持有EntrySet,继承自AbstractSet,必须实现iterator()size()),Map必须实现entrySet(),返回的就是EntrySet

 

 

关系:

MapEntry内部类和entrySet()方法;Entry内部类持有索引、getkey()getValue()setValue(),注意!没有setKey();

entrySet()需要产生EntrySet,他持有EntrySet,并且实现size()iterator();

iterator()就需要产生Iterator,可以在EntrySet内部自己定义Iterator,每个迭代器只有一个Entrynext()使得Entry的索引变化。

 

 

 

 

3.LinkedList独有的方法;addFirst() getFirst() removeFirst() removeLast()

 

 

4.Set:

 

Set:存入的元素应定义equals()方法。

HashSet:它应该是你的默认首选,因为它对速度进行了优化,存入的元素应定义hashCode()方法。

TreeSet:元素必须实现Comparable接口。(排序容器中的元素必须实现Comparable接口)

LinkedHashSet:具有HashSet的查询速度,内部用链表维护元素的次序。元素也定义hashCode()方法。

 

 

通常会希望compareTo()可以产生与equals()方法一致的排序顺序,如果equals()返回truecompareTo()应该为0

 

 

一个烦人的错误:对于没有实现hashCode()的元素,当你将它应用于任何散列结构中都会产生重复值,这样就违反了set的不重复原则。

 

 

SortedSet:

当你想改变它的排序次序的时候,你可以生成自己的Comparator,在comparator()返回你自定义的Comparator.

first() last () subSet() headSet(toElement) tailSet(fromElement)

 

 

 

5.Map:

A.)任何key都必须具有一个equals()方法.

B.)如果key被用于散列,那么必须具备恰当的hashCode()方法。

C.)如果key被用于TreeMap,那么它必须实现Comparable

 

    keySet() values() containsKey/Value()

 

D.)SortetMap:

comparator() firstKey() lastkey() subMap(fromkey,tailkey) headMap(fromkey)tailMap(tailkey)

 

 

E.)LinkedHashMap需要注意的一点:

可以在构造器中设定LinkedHashMap使用LRU算法,于是没有访问的元素就会出现在队列的前面,对于需要定期清理元素以节省空间的程序来说,使用它是一个好的思路。

 

 

 

Map<Integer,Integer>map =new LinkedHashMap<Integer,Integer>(16, (float) 0.75, true);

map.put(0, 0);

map.put(1, 1);

map.put(2, 2);

map.put(3, 3);

map.put(4, 4);

map.put(5, 5);

map.put(6, 6);

map.put(7, 7);

map.put(8, 8);

System.out.println(map);

for(int i=0;i<5;i++){

map.get(i);

}

System.out.println(map);

map.get(5);

System.out.println(map);

 

 

{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8}

{5=5, 6=6, 7=7, 8=8, 0=0, 1=1, 2=2, 3=3, 4=4}

{6=6, 7=7, 8=8, 0=0, 1=1, 2=2, 3=3, 4=4, 5=5}

 

 

 

 

 

 

 

 

 

 

 

6.一个很好的技巧:

 

class Example  {

public <T>void exmaple(Class<T>type) {

Map map =new HashMap();

Constructor<T>c =type.getConstructor();

for(int i=0;i<10;i++){

map.put(c.newInstance(null),i);

}

}

 

这个方法可以使用反射机制使得这个方法应用于任何我们想使用的类及其子类,当我们有多个子类的时候,这个技巧就很好。

 

 

7.默认的hashCode()使用的是对象的地址计算散列码。

 

8.默认的equals()比较的是对象的地址。

 

9.自己通过双list实现Map:

class Mymap<K,V>extends AbstractMap<K,V>  {

List<K>keys =new ArrayList<K>();

List<V>values=new ArrayList<V>();

@Override

public V put(K key, Vvalue) {

Vv = get(key);

if(!keys.contains(key)){

keys.add(key);

values.add(value);

}else{

int i =keys.indexOf(key);

values.set(i, value)

}

return v;

}

@Override

public V get(Object key) {

if(!keys.contains(key)){

return null;

}

return values.get(keys.indexOf(key));

}

 

@Override

public Set<java.util.Map.Entry> entrySet() {

Set<java.util.Map.Entry>set =new HashSet<java.util.Map.Entry>();

Iterator<K>  ki =  keys.iterator();

Iterator<V>  vi =  values.iterator();

while(ki.hasNext()){

set.add(new Entry<K, V>(keys.iterator(),values.iterator()));

}

return set;

}

}

 

 

10.为速度而散列:

key进行查询的时候,线性查询是一种很慢的方式,更进一步可以选择Collections.binarySearch(),更进一步可以选择散列。

 

存储一组元素最快的数据结构是数组,因此用它来存储key的信息(不是key,而是key的散列码),为解决数组容量被固定的问题,不同的key可能产生相同的hashCode,也就是产生冲突,并且数组并不直接保存value,而是保存valuelist。然后对list中的值使用equals()进行线性查询。这部分查询比较慢,但是如果你的散列函数好的话,数组的每个位置只有很少的元素,这样速度就会提升很多了。

 

 

 

11.自己实现一个简单的HashMap:

 

 

 

class  SimpleHashMap<K,V>extends AbstractMap<K,V>{

public final int SIZE = 1000;

LinkedList<MapEntry<K,V>>[]  buckets =  new LinkedList[SIZE];

                                                                                                                             

@Override

public V put(K key, Vvalue) {

//先比较索引处又没有LinkedList,如果有的话比较LinkedListz中各MapEntry中的key有没有和我们的key相同的,有则替换,没有则在listz中添加我们的MapEntry

int index = Math.abs(key.hashCode()%SIZE);

 VoldValue =null;

LinkedList<MapEntry<K,V>>list  ;

boolean found =false;

if(!(buckets[index] == null)){

list =buckets[index];

MapEntry<K,V>entry =new MapEntry(key,value);

//接下来迭代list,进行对比

ListIterator<MapEntry<K,V>>iterator =list.listIterator();

while(iterator.hasNext()){

 MapEntry<K,V>get  =iterator.next();

 if(get.getKey().equals(key)){

 oldValue =get.getValue();

 iterator.set(entry);

 found =true;

 break;

 }

}

if(!found){

list.add(entry);

}

}

buckets[index] = new LinkedList<MapEntry<K,V>>();

MapEntry<K,V>entry =new MapEntry<K, V>(key,value);

buckets[index].add(entry);

 

return oldValue;

}

@Override

public V get(Object key) {

int index = Math.abs(key.hashCode()%SIZE);

if(buckets[index] == null){

return null;

}

LinkedList<MapEntry<K, V>> list =buckets[index];

for(MapEntry entry :list){

if(entry.getKey().equals(key)){

return (V)entry.getValue();

}

}

return null;

}

 

public Set<Map.Entry<K,V>> entrySet() {

Set<Map.Entry<K,V>>set =new HashSet<Map.Entry<K,V>>();

for(LinkedList<MapEntry<K,V>>list :buckets){

if(list ==null){

continue;

}

for(MapEntry<K,V>entry :list){

set.add(entry);

}

}

 

return set;

}

}

 

 

 

 

class MapEntry<K,V>implements Map.Entry<K, V>{

Kkey;

Vvalue;

public MapEntry(Kkey, Vvalue) {

this.key =key;

this.value =value;

}

 

@Override

public K getKey() {

return key;

}

 

@Override

public V getValue() {

return value;

}

 

@Override

public V setValue(V v) {

Vresult =v;

this.value  =v;

return result;

}

@Override

public int hashCode() {

return (key==null? 0 :key.hashCode()) ^ (value ==null ? 0:value.hashCode());

}

public  boolean equlas(Objecto){

if (!(o instanceof MapEntry)){

return false;

}

MapEntry obj = (MapEntry)o;

return (key ==null ?obj.getKey() ==null:key.equals(obj.getKey())) &&

(value ==null?obj.getValue() ==null :value.equals(obj.getValue()));

}

}

 

 

public class Test {

@SuppressWarnings("rawtypes")

public static void main(String[]args) {

SimpleHashMapmap =new SimpleHashMap();

map.put("1","第1");

map.put("2","第2");

map.put("3","第3");

map.put("4","第4");

map.put("5","第5");

System.out.println(map);

System.out.println(System.currentTimeMillis());

System.out.println(map.get("3"));

System.out.println(map.get("3"));

System.out.println(map.get("3"));

System.out.println(map.get("3"));

System.out.println(map.get("3"));

System.out.println(map.get("3"));

System.out.println(map.get("3"));

 

System.out.println(System.currentTimeMillis());

System.out.println(map.entrySet());

}

 

}

 

 

 

{4=第4, 5=第5, 1=第1, 2=第2, 3=第3}

1485312696662

3

3

3

3

3

3

3

1485312696663

[MapEntry@eeabc, MapEntry@eeabc, MapEntry@eeab4, MapEntry@eeab4, MapEntry@eeab4]

 

 

 

12.覆盖hashCode():

 

设计hashCode()最重要的因素就是:无论何时,对于同一个对象都应该生成相同的hashCode

 

 

String有个特点:如果你的程序中有多个String对象,并且内容相同,那么这些String对象会映射到同一块内存区域。即:s1 = new String(hi)s2 = new String(hi)的内存区域相同。默认的hashCode()方法生成的hashCode则相同,所以对于String,默认的hashCode()比较的是内容。

 

 

 

另外一点:好的hashCode()应该产生分布均匀的散列码,因为如果你的散列码都集中在一块,那么你的散列容器会在某一区域负载严重,这将导致你的查询速度很慢(数组中存list,list中的线性查找速度很慢)

 

 

 

 

 

13.LinkedHashMap在插入的时候比HashMap慢一点,因为它维护散列数据结构的同时还要维护链表,但是正是因为这个链表,它迭代的速度会更快。

 

 

 

14.Collections的方法:

 

a.)各种checkedCollection()方法,产生Colletcion或者Collection的具体子类型的动态类型的安全的视图。

b.)max(Collection) min(Colletion) 返回Collcetion中最大或最小的元素,采用自然比较法。

c.)max(Collection,Compatatopr) min(Collection,Comparator),采用Compartor的次序进行排序。

d.)indexOfSublist(list s,list t) lastIndexOfSublist(list s,list t)

e.)replaceAll(List,oldValue , new Val)

f.)reverse()

g.)reverseOrder()返回一个Comparator

h.)reverseOrder(Comparator<T>)  逆转了Comparator的次序

i.)rotate(List , int distance) 所有元素向后移动distance个位置,末位的元素循环到前面来

j.)shuffle()

k.)sort() sort(list.Comparator)

l.)copy(List dest,List src)

m.)swap(list,int i, int j) 交换list中位置iJ的元素,速度很快。

n.)fill(list)

o.)nCopies()

p.)disjoint(Collection,Collection)  当两个集合没有任何相同元素的时候,返回true

q.)frequency(CollectionObject x) 返回集合中等于x的元素个数

r.)emptyList() emptyMap() emptySet()

s.)singleton(T x) 产生不可变的Set<T>

t.)singletonList(T x) 产生不可变的List<T>

u.)singletonMap(K key , V value)  产生不可变的Map<T>

 

 

 

 

15.unmodifiable容器:

Collections.各种unmodifiable(),如下图:

 

 

 

 

 

 

16.synchronize容器:

 

Collections.各种synchronized:

 

 

 

 

17.快速报错机制:

 Collection<String>c =new ArrayList<String>();

 Iterator<String>i =c.iterator();

 c.add("a");

 Strings =i.next();

 

 

 

Exception in thread "main"java.util.ConcurrentModificationException

at java.util.ArrayList$Itr.checkForComodification(Unknown Source)

at java.util.ArrayList$Itr.next(Unknown Source)

at Test.main(Test.java:45)

 

在容器取得迭代器之后没,又有东西被放入到了该容器中。当程序的不同部分修改同一个容器的时候,就可能导致容器状态不一致,抛出异常,我们应该在添加完元素之后再获得迭代器。

 

 

 

Java容器类采用快速报错机制,它会探查容器上的任何除了你的进程所进行的操作以外的所有变化,一旦它发现其他进程修改了容器,就会立即抛出ConcurrentModificationException异常,这就是快速报错机制。

 

ConcurrentHashMapCopyOnWriteArrayListCopyOnWriteArraySet都使用了可以避免ConcurrentModificationException的技术。

 

 

 

18.Stack继承自Vecior,它拥有了Vector的所有特点和行为,再加上一些额外的行为。

猜你喜欢

转载自blog.csdn.net/mypromise_tfs/article/details/54729985
今日推荐