Java 泛型和集合小结 官方文档

Java 泛型集合小结

There are three ways to traverse collections: (1) using aggregate operations (2) with the for-each construct and (3) by using Iterators.
https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html

List 代码片段

//https://docs.oracle.com/javase/tutorial/collections/interfaces/list.html
public static <E> void swap(List<E> a, int i, int j) {
    E tmp = a.get(i);
    a.set(i, a.get(j));
    a.set(j, tmp);
}
public static void shuffle(List<?> list, Random rnd) {
    for (int i = list.size(); i > 1; i--)
        swap(list, i - 1, rnd.nextInt(i));
}

打乱顺序

import java.util.*;

public class Shuffle {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (String a : args)
            list.add(a);
        Collections.shuffle(list, new Random());
        System.out.println(list);
    }
}
//更快
import java.util.*;

public class Shuffle {
    public static void main(String[] args) {
        List<String> list = Arrays.asList(args); //直接初始化
        Collections.shuffle(list);
        System.out.println(list);
    }
}

倒序遍历list,listIterator不带参数时返回list的起始位置,带参数时返回指定位置,制定i,返回第i-1个位置。

for (ListIterator<Type> it = list.listIterator(list.size()); it.hasPrevious(); ) {
    Type t = it.previous();
    ...
}

迭代器出现的位置,在缝隙中
possible-cursor
定位元素

public int indexOf(E e) {
    for (ListIterator<E> it = listIterator(); it.hasNext(); )
        if (e == null ? it.next() == null : e.equals(it.next()))
            return it.previousIndex();
    // Element not found
    return -1;
}

//改
public static int indexOf(List<Integer> list, Integer e) {
    for (ListIterator<Integer> it = list.listIterator(); it.hasNext(); )
        if (e == null ? it.next() == null : e.equals(it.next()))
            return it.previousIndex();
    // Element not found
    return -1;
}


替换元素

public static <E> void replace(List<E> list, E val, E newVal) {
    for (ListIterator<E> it = list.listIterator(); it.hasNext(); )
        if (val == null ? it.next() == null : val.equals(it.next()))
            it.set(newVal);
}

replace all occurrences of a specified value with the sequence of values contained in the specified list.

public static <E> 
    void replace(List<E> list, E val, List<? extends E> newVals) {
    for (ListIterator<E> it = list.listIterator(); it.hasNext(); ){
        if (val == null ? it.next() == null : val.equals(it.next())) {
            it.remove(); // 移除指定元素后
            for (E e : newVals) // 插入新的一组元素
                it.add(e);
        }
    }
}

Range-view Operation
subList(int fromIndex, int toIndex) 等价于下面的代码

for (int i = fromIndex; i < toIndex; i++) {
    ...
}

定位元素

int i = list.subList(fromIndex, toIndex).indexOf(o);
int j = list.subList(fromIndex, toIndex).lastIndexOf(o);
//Note that the preceding idioms return the index of the found element in the subList,
//not the index in the backing List.

扑克抽牌

public static <E> List<E> dealHand(List<E> deck, int n) {
    int deckSize = deck.size();
    List<E> handView = deck.subList(deckSize - n, deckSize);
    List<E> hand = new ArrayList<E>(handView);
    handView.clear();
    return hand;
}

扑克牌例子

import java.util.*;

public class Deal {
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("Usage: Deal hands cards");
            return;
        }
        int numHands = Integer.parseInt(args[0]);
        int cardsPerHand = Integer.parseInt(args[1]);
    
        // Make a normal 52-card deck.
        String[] suit = new String[] {
            "spades", "hearts", 
            "diamonds", "clubs" 
        };
        String[] rank = new String[] {
            "ace", "2", "3", "4",
            "5", "6", "7", "8", "9", "10", 
            "jack", "queen", "king" 
        };

        List<String> deck = new ArrayList<String>();
        for (int i = 0; i < suit.length; i++)
            for (int j = 0; j < rank.length; j++)
                deck.add(rank[j] + " of " + suit[i]);
    
        // Shuffle the deck.
        Collections.shuffle(deck);
    
        if (numHands * cardsPerHand > deck.size()) {
            System.out.println("Not enough cards.");
            return;
        }
    
        for (int i = 0; i < numHands; i++)
            System.out.println(dealHand(deck, cardsPerHand));
    }
  
    public static <E> List<E> dealHand(List<E> deck, int n) {
        int deckSize = deck.size();
        List<E> handView = deck.subList(deckSize - n, deckSize);
        List<E> hand = new ArrayList<E>(handView);
        handView.clear();
        return hand;
    }
}
Running the program produces output like the following.

% java Deal 4 5

[8 of hearts, jack of spades, 3 of spades, 4 of spades,
    king of diamonds]
[4 of diamonds, ace of clubs, 6 of clubs, jack of hearts,
    queen of hearts]
[7 of spades, 5 of spades, 2 of diamonds, queen of diamonds,
    9 of clubs]
[8 of spades, 6 of diamonds, ace of spades, 3 of hearts,
    ace of hearts]

泛型 Generics

https://docs.oracle.com/javase/tutorial/extra/generics/intro.html

引入

List myIntList = new LinkedList(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = (Integer) myIntList.iterator().next(); // 3 
//vs
List<Integer> 
    myIntList = new LinkedList<Integer>(); // 1'
myIntList.add(new Integer(0)); // 2'
Integer x = myIntList.iterator().next(); // 3'

In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is some generic type declaration, it is not the case that G<Foo> is a subtype of G<Bar>.
https://docs.oracle.com/javase/tutorial/extra/generics/subtype.html

// 不含泛型的写法
void printCollection(Collection c) {
    Iterator i = c.iterator();
    for (k = 0; k < c.size(); k++) {
        System.out.println(i.next());
    }
}
// 不用通配符的泛型,失去了包容不同集合的优势
void printCollection(Collection<Object> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}
// 使用 ?, now, we can call it with any type of collection
void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}

Collection<?> 带 ? 通配符的集合,不能用add()添加元素,因为不知道应该添加什么类型。但是可以用get()取元素,因为知道取出来的一定是个 Object.

有界通配符 Bounded Wildcards

public void drawAll(List<Shape> shapes) {
    for (Shape s: shapes) {
        s.draw(this);
   }
}
// 画各种图形的列表,包含Shape和Shape的子类
public void drawAll(List<? extends Shape> shapes) {
    ...
}
// 由于在方法体内,仍不知道?是何种类型,所以只能读,不能写(如add)

人口普查的例子,汽车部门向人口普查局提供司机清单,Driver extends Person
For example, if the department of motor vehicles supplies a list of drivers to the census bureau

public class Census {
    public static void addRegistry(Map<String, ? extends Person> registry) {
}
...

Map<String, Driver> allDrivers = ... ;
Census.addRegistry(allDrivers);

泛型方法

https://docs.oracle.com/javase/tutorial/extra/generics/methods.html

// Recall that you cannot just shove objects into a collection of unknown type.
static void fromArrayToCollection(Object[] a, Collection<?> c) {
    for (Object o : a) { 
        c.add(o); // compile-time error
    }
}
// Just like type declarations, method declarations can be generic—that is, parameterized by one or more type parameters.
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
        c.add(o); // Correct
    }
}

究级问题:when should I use generic methods, and when should I use wildcard types?

官方文档表述摘录如下

However, in both containsAll and addAll, the type parameter T is used only once. The return type doesn’t depend on the type parameter, nor does any other argument to the method (in this case, there simply is only one argument). This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites. If that is the case, one should use wildcards. Wildcards are designed to support flexible subtyping, which is what we’re trying to express here.

Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn’t such a dependency, a generic method should not be used.

去掉通配符的情况

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src) {
    ...
}
// 去掉通配符
class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}
// 使用通配符比声明显式类型参数更清晰、更简洁,因此应该尽可能首选通配符。

例子,记录绘画历史

static List<List<? extends Shape>> 
    history = new ArrayList<List<? extends Shape>>();

public void drawAll(List<? extends Shape> shapes) {
    history.addLast(shapes);
    for (Shape s: shapes) {
        s.draw(this);
    }
}
// 泛型类的所有实例都具有相同的运行时类,
List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());// true
//You might be tempted to say false, but you'd be wrong. It prints true
<? super T> 表示 T本身以及T的父类。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值