[英]Why no tail() or head() method in List to get last or first element?
最近和同事討論了為什么Java中的List接口沒有head()
和tail()
方法。
為了實現這樣的功能,必須編寫一個看起來像這樣的包裝器:
public E head() {
if (underlyingList == null || underlyingList.isEmpty())
return null;
return underlyingList.get(0);
}
public E tail() {
if (underlyingList == null || underlyingList.isEmpty())
return null;
return underlyingList.get(underlyingList.size()-1);
}
我不知道所有 List 實現,但我認為至少在 LinkedList 和 ArrayList 中,獲取最后一個和第一個元素(恆定時間)應該很簡單。
所以問題是:
為任何 List 實現提供 tail 方法不是一個好主意是否有特定的原因?
List接口有subList
,幾乎是head
和tail
。 你可以按如下方式包裝它
public List head(List list) {
return list.subList(0, 1);
}
public List tail(List list) {
return list.subList(1, list.size());
}
編輯
根據@Pablo Grisafi的回答,這是一個Java快速排序實現 - 不是通用的,也不是高效的。 正如預期的那樣head()
應該返回一個元素 - 而不是列表。
public class QSort {
public static List<Integer> qsort(List<Integer> list) {
if (list.isEmpty()) {
return list;
} else {
return merge(
qsort(lesser
(head(list), tail(list))),
head(list),
qsort(greater(
head(list), tail(list)))
);
}
}
private static Integer head(List<Integer> list) {
return list.get(0);
}
private static List<Integer> tail(List<Integer> list) {
return list.subList(1, list.size());
}
private static List<Integer> lesser(Integer p, List<Integer> list) {
return list.stream().filter(i -> i < p).collect(toList());
}
private static List<Integer> greater(Integer p, List<Integer> list) {
return list.stream().filter(i -> i >= p).collect(toList());
}
private static List<Integer> merge(List<Integer> lesser, Integer p, List<Integer> greater) {
ArrayList list = new ArrayList(lesser);
list.add(p);
list.addAll(greater);
return list;
}
public static void main(String[] args) {
System.out.println(qsort(asList(7, 1, 2, 3, -1, 8, 4, 5, 6)));
}
}
Java Collections Framework由Joshua Bloch編寫。 他的API設計原則之一是: 高功率重量比 。
tail()
和head()
可以通過get()
和size()
,因此沒有必要將tail()
和head()
到非常通用的接口java.util.List
。 一旦用戶使用這些方法,您就沒有機會刪除它們,您必須永遠保留這些不必要的方法。 那很糟。
如果要遞歸處理列表(通常是函數編程中使用的head / tail),可以使用Iterator。
Integer min(Iterator<Integer> iterator) {
if ( !iterator.hasNext() ) return null;
Integer head = iterator.next();
Integer minTail = min(iterator);
return minTail == null ? head : Math.min(head, minTail);
}
據我所知, List
沒有element
方法。 但是, LinkedList
有getFirst()
和getLast()
,它們就像你描述的那樣。
在我看來,尾巴和頭部更熟悉具有功能背景的人。 當你開始傳遞函數時,它們是非常有用的,這就是大多數函數式語言實現它們的原因,甚至還有快捷符號來引用它們,比如haskell甚至是scala(即使它不是那么實用,我知道)
在“(幾乎)所有東西都是一個對象,但方法是以程序的方式制作”java世界,當傳遞函數至少很難並且總是很尷尬時,頭/尾方法不是那么有用。
例如,檢查quicksort的這個haskell實現:
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
除其他外,它依賴於容易分離頭部和尾部的能力,還依賴於能夠使用謂詞過濾集合。 java實現(檢查http://www.vogella.de/articles/JavaAlgorithmsQuicksort/article.html )看起來完全不同,它是低級別的,並且不依賴於分離頭部和尾部。
注意:下一句話是完全主觀的,基於我的個人經驗,可能被證明是錯誤的,但我認為這是真的:
函數式編程中的大多數算法都依賴於頭/尾,在程序編程中,您依賴於訪問給定位置的元素
在良好的API設計中總會有選擇。 有許多方法可以添加到API中,但是,您必須找到使API可用於大多數人並使其過於混亂和冗余之間的細微差別。 實際上,您可以實現tail方法,因為您已經以高效的方式顯示了大多數List實現,並且LinkedList已經有了getLast()方法。
您應該使用列表,以使其更容易。 當你在一個列表上使用遞歸時,你必須這樣想……這個列表有一個頭(第一個元素)和一個尾(除了頭之外的所有其他元素)。 使用遞歸,你需要在頭部做你想做的事,然后在尾部調用 function,這樣你總是有一個 size = size - 1 的列表
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(11);
list.add(12);
list.add(0);
list.add(3);
list.add(1);
list.add(4);
list.add(11);
System.out.println(countOccurrences(list, 11));
}
public static int countOccurrences(List<Integer> list, int n) {
if (list.size() == 0) {//if list is empty return 0
return 0;
}else {
if(list.get(0) == n) {//if head of list is equal to n add 1 and call the function on the tail of the list
return 1 + countOccurrences(list.subList(1, list.size()), n);
}else {//else if it's not equal to n call the function on the tail of the list without adding 1
return countOccurrences(list.subList(1, list.size()), n);
}
}
}
您可以在 Stream 上獲得這些列表:
頭
myList.stream().findFirst() // Optional<T>, return empty for empty list
尾巴(傳統意義)
myList.stream().skip(1).collect(toList()) // or don't collect & continue with a Stream
最后一個(如果列表是無限的,可能很危險:):
myList.stream().reduce((a,b) -> b) // Optional<T>, return empty for empty list
head()
通過list.iterator()。next(),list.get(0)等提供。
如果列表與尾指針雙重鏈接,或者基於數組等,則提供tail()是合理的。這些方面都沒有為List接口本身指定。 否則它可能具有O(N)性能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.