• 轻量级集合包装器:
  • Arrays类的静态方法asList将返回一个包装了普通Java数组的List包装器。返回的是一个视图对象,带有访问底层数组的get和set方法,但改变数组大小的所有方法都将抛出一个UnsupportedOperationException异常
package asList;

import java.util.*;
public class AsListTest {
	public static void main( String[] args ) {
		String[] strs = { "I", "love", "you" };
		List<String> strList = Arrays.asList(strs);
		for( String s : strs )
			System.out.print(s + " ");
		System.out.println();
		strList.set(1, "hate");
		for( String s : strs )
			System.out.print(s + " ");
		System.out.println();
	}
}
  • 子范围:
  • 可以为很多集合建立子范围(subrange)视图。左闭右开区间[a,b)。List group2 = staff.subList(10,20),取出第10个到第19个元素。
  • 对于有序集和映射,可以使用排序顺序而不是元素位置建立子范围。
  • SortedSet<E> subSet(E from, E to)
  • SortedSet<E> headSet(E to)
  • SortedSet<E> tailSet(E from)
  • 这些方法将返回大于等于from小于to的所有元素子集。
  • 映射视图也类似 SortedMap<K,V> subMap( K from, K to) /headMap/tailMap。
  • Java SE 6引入的NavigableSet接口,更加灵活,可以指定是否包含边界
package subList;

import java.util.*;
public class SubListTest {
	public static void main( String[] args ) {
		List<String> lst = Arrays.asList( "Love","me","like","you","do" );
		ArrayList<String> list = new ArrayList<>(lst);
		List<String> sub = list.subList(2, 5);
		for( String s : list ) System.out.print(s + " ");
		System.out.println();
		sub.clear();
		for( String s : list ) System.out.print(s + " " );
		System.out.println();
		
		List<String> unsortedLst = Arrays.asList( "Banana", "Apple", "Cucumber", "Eggplant", "Durian", "Flower");
		NavigableSet<String> sorted = new TreeSet<>(unsortedLst);
		for( String s : sorted ) System.out.print(s + " ");
		System.out.println();
		NavigableSet<String> sub2 = sorted.subSet("Cucumber", false, "Eggplant", true );
		sub2.clear();
		for( String s : sorted ) System.out.print(s + " ");
		System.out.println();
	}
}
  • 不可修改的视图:Collections还有几个(静态)方法,用于产生集合的不可修改视图unmodifiable views)。
  • Collections.unmodifiableCollection
  • Collections.unmodifiableList //返回一个实现List接口的类对象。
  • Collections.unmodifiableSet
  • Collections.unmodifiableSortedSet
  • Collections.unmodifiableNavigableSet
  • Collections.unmodifiableMap
  • Collections.unmodifiableSortedMap
  • Collections.unmodifiableNavigableMap
  • 每个方法都定义于一个接口,可以调用接口中的所有方法而不只是访问器,但是所有的更改器方法已经被重新定义为抛出UnsupportedOperationException异常,而不是将调用传递给底层。
  • 同步视图:如果由多个线程访问集合,就必须确保集不会被意外地破坏。
  • 类库的设计者使用视图机制确保常规集合的线程安全,而不是实现线程安全的集合类
  • 如:Collections类的静态synchronizedMap方法,可以将任何一个映射表转换成具有同步访问方法的Map。
  • Map<Sring,Employee> map = Collections.synchronizedMap( new HashMap<String, Employee>() ) 。现在就可以多线程访问map对象了,像get和put这类方法都是同步操作的,即在另一个线程调用另一个方法之前,刚才的方法必须彻底完成
  • 受查视图:用来对泛型类型发生问题时提供调试支持
  • 实际上,将错误类型的元素混入泛型集合中的问题极有可能发生。
  • 受查视图可以探测到这类问题。
  • List<String> safeStrings = Collections.checkedList( Strings, String.class )
  • 这样的做法的好处就是让错误能够在正确的位置得以报告
package checkedList;

import java.util.*;
import java.time.*;
/*
当rawList = safeStrings时:运行rawList.add(new Date()) 就会抛出异常
当rawList = strings时,程序运行不抛出异常,但此时 strings 里面包含了一个Date对象!
*/
public class CheckedListTest {
	public static void main( String[] args ) {
		ArrayList<String> strings = new ArrayList<>();
		List<String> safeStrings = Collections.checkedList(strings, String.class);
		//List rawList = safeStrings;
		List rawList = strings;
		rawList.add(new Date()); 
		
	}
}
  • 关于可选操作的说明:
  • 通常,视图有一些局限性,即可能只可以读,无法改变大小,只支持删除而不支持插入。如果操作不恰当,会抛出UnsupportedOperationException异常。
  • 在集合和迭代器接口的API文档中,许多方法描述为“可选操作”。
  • 所谓“可选操作”,就是视图虽然实现了接口,但是却没有真正意义上实现了接口中的所有方法。理论上视图可以调用接口中的所有方法,然而,某些方法调用就会导致抛出UnsupportedOperationException。

 

 

  • 算法:泛型集合接口有一个很大的优点,即算法只需要实现一次
  • max方法实现为能够接收任何实现了Collection接口的对象。
public static <T extends Comparable> T max( Collection<T> c )
{
    if(c.isEmpty()) throw new NoSuchElementException();
    Iterator<T> iter = c.iterator();
    T largest = iter.next();
    while( iter.hasNext() ){
        T next = iter.next();
        if( largest.compareTo(next) < 0 ) largest = next;
    }
    return largest;
}
  • 排序与混排
  • Collections类的sort方法可以对实现了List接口的集合进行排序(这个方法假定列表元素实现了Comparable接口)
  • Collections.sort(staff)
  • List接口的sort方法
  • staff.sort( Comparator.comparingDouble( Employee::getSalary ) )
  • 降序:
  • staff.sort( Comparator.reverseOrder() )  //按照compareTo逆序
  • staff.sort( Comparator.comparingDouble( Employee::getSalary).reversed() ) //按照提供的比较器逆序
  • sort方法的实现,在Java中,直接将所有元素转入一个数组,再对数组进行排序(归并排序),再将排序后的序列复制回列表
  • 归并排序有一个主要的优点:稳定,即不需要交换相同的元素。归并排序之后,原来序列相同的元素,相对位置不会发生改变。
  • 所有接受集合参数的方法必须描述什么时候可以安全地将集合传递给算法:
  • 如果列表支持set方法,则是可修改的。
  • 如果列表支持add和remove方法,则是可改变大小的。
  • Collections类有一个算法 shuffle,功能是随机混排列表中的元素顺序。
  • 如果提供的列表没有实现RandomAccess接口,shuffle方法将元素复制到数组中,然后打乱数组元素的顺序,最后复制回列表中。
  • 示例程序:shuffle算法的应用
package shuffle;

import java.util.*;
public class ShuffleTest {
	public static void main( String[] args ) {
		List<Integer> list = new ArrayList<>();
		
		for( int i = 1; i <= 50; i++ ) list.add(i);
		
		System.out.println(list);
		Collections.shuffle(list);
		List<Integer> sub = list.subList(0, 6);
		sub.sort(Comparator.naturalOrder());
		System.out.println(sub);
	}
}
  • 二分查找:
  • Collections类的binarySearch方法实现了二分查找算法。
  • 集合必须是有序的。
  • 集合必须实现了List接口。
  • i = Collections.binarySearch(c,element)
  • i = Collections.binarySearch(c,element,comparator)
  • 如果返回的数值大于等于0,则是正确的索引。如果返回负值,则没有匹配的对象,可以利用返回值计算应该将element插入到集合的哪个位置,插入的位置是 - i - 1
  • 只有采用随机访问,二分查找才有意义。
  • java.util.Collections
  • static <T extends Comparable<? super T>> T min( Collection<T> elements )
  • 同上max
  • static <T> T min( Collection<T> elements, Comparator<? super T> c )
  • 同上max
  • static <T> void copy( List<? super T> to, List<T> from )
  • static <T> void fill( List<? super T> l, T value )
  • static <T> boolean addAll( Collection<? super T> c, T... values )
  • static <T> boolean replaceAll( List<T> l, T oldValue, T newValue )
  • static int indexOfSubList( List<?> l, List<?> s )
  • static int lastIndexOfSubList( List<?> l, List<?> s )
  • static void swap( List<?> l, int i, int j )
  • static void reverse( List<?> l )
  • static void rotate( List<?> l, int d )  //将索引条目移动到位置 (i+d)%l.size()的位置上
  • static int frequency( Collection<?> c, Object o )
  • boolean disjoint( Collection<?> c1, Collection<?> c2 )
  • 批操作:
  • removeAll
  • retainAll
  • addAll

 

  • 集合与数组的转换
  • 数组转换为集合: Arrays.asList
  • toArray()方法(返回的是一个Object[]数组);更好的方式: toArray( new Xxx[n] )/ toArray( new Xxx[0] )
  • 编写自己的算法:
  • 编写自己的算法(使用集合作为参数),应该尽可能地使用接口,而不要使用具体的实现。因为这样会限制方法的调用。
  • 编写一个返回集合的方法,也尽量返回一个接口,便于日后方便修改实现。

 

  • 遗留的集合:
  • HashTable类:
  • 与HashMap的作用一样,实际上也拥有相同的接口
  • 如果对同步性以及遗留代码的兼容性没有要求,应该使用 HashMap。
  • 如果需要并发访问,则要使用 ConcurrentHashMap
  • 枚举:
  • 遗留集合使用 Enumeration接口 对元素序列进行遍历。
  • 这个接口有两个方法:hasMoreElements 和 nextElement 方法。(与hasNext方法 和 next方法 类似)
  • 静态方法 Collections.enumeration产生一个枚举对象,枚举集合中的元素。
  • java.util.HashTable<K,V>
  • Enumeration<K> keys()
  • Enumeration<V> elements()
  • java.util.Vector<E>
  • Enumeration<E> elements()
  • 传递集合要比传递迭代器更为明智,若需要,总是可以从集合中获得迭代器。还能使用集合中的所有方法。
  • 属性映射(property map):非常特殊的映射结构
  • 键与值都是字符串
  • 表可以保存到一个文件中,也可以从文件中加载。
  • 使用一个默认的辅助表。
  • 使用 属性映射的Java平台类称为 Properties
  • java.util.Properties
  • Properties()
  • Properties( Properties defaults )
  • String getProperty( String key )
  • String getProperty( String key, String defaultValue )
  • void load( InputStream in )
  • void store( OutputStream out, String commentString )
  • 栈:
  • java.util.Stack<E>
  • E push(E item)
  • E pop()
  • E peek()
  • 位集:
  • java.util.BitSet
  • BitSet( int initialCapacity)
  • int length()
  • boolean get( int bit )
  • void set( int bit )
  • void clear( int bit )
  • void and( BitSet set )
  • void or( BitSet set )
  • void xor( BitSet set )
  • void andNot( BitSet set )
  • 示例程序:(筛质数)
package sieve;

import java.util.*;
public class Sieve {
	public static void main( String[] args ) {
		int n = 200_0000;
		long start = System.currentTimeMillis();
		
		BitSet bSet = new BitSet( n + 1 );
		
		for( int i = 2; i <= n; i++ ) bSet.set(i);
		int count = 0;
		
		int i; 
		for( i = 2; i * i <= n; i++ ) {
			if( bSet.get(i) ) {
				count++;
				int k = 2 * i;
				while( k <= n ) {
					bSet.clear(k);
					k += i;
				}
			} 
		}
		while( i <= n ) {
			if( bSet.get(i) ) count++;
			i++;
		}
		long end = System.currentTimeMillis();
		System.out.println( count + " primes.(within 200 0000)");
		System.out.println("time cost: " + (end - start) + " milliseconds.");
	}
}