为什么List中没有tail()或head()方法来获取last或first元素?

我最近与同事讨论了为什么Java中的List接口没有head()tail()方法。

为了实现这样的function,必须编写一个看起来像这样的包装器:

 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方法不是一个好主意?

Java Collections Framework由Joshua Bloch编写。 他的API设计原则之一是: 高功率重量比

tail()head()可以通过get()size() ,因此没有必要将tail()head()到非常通用的接口java.util.List 。 一旦用户使用这些方法,您就没有机会删除它们,您必须永远保留这些不必要的方法。 那很糟。

List接口有subList ,几乎是headtail 。 你可以按如下方式包装它

 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 qsort(List 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 list) { return list.get(0); } private static List tail(List list) { return list.subList(1, list.size()); } private static List lesser(Integer p, List list) { return list.stream().filter(i -> i < p).collect(toList()); } private static List greater(Integer p, List list) { return list.stream().filter(i -> i >= p).collect(toList()); } private static List merge(List lesser, Integer p, List 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))); } } 

如果要递归处理列表(通常是函数编程中使用的head / tail),可以使用Iterator。

 Integer min(Iterator iterator) { if ( !iterator.hasNext() ) return null; Integer head = iterator.next(); Integer minTail = min(iterator); return minTail == null ? head : Math.min(head, minTail); } 

据我所知, List没有element方法。 但是, LinkedListgetFirst()getLast() ,它们就像你描述的那样。

在我的拙见中,尾巴和头部更熟悉具有function背景的人。 当你开始传递函数时,它们是非常有用的,这就是大多数函数式语言实现它们的原因,甚至还有快捷符号来引用它们,比如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 )看起来完全不同,它是低级别的,并且不依赖于分离头部和尾部。
注意:下一句话是完全主观的,并且基于我的个人经验,可能被certificate是错误的,但我认为这是真的:
函数式编程中的大多数算法都依赖于头/尾,在程序编程中,您依赖于访问给定位置中的元素

在良好的API设计中必须有一个必须做出的选择。 有许多方法可以添加到API中,但是,您必须找到使API可用于大多数人并使其过于混乱和冗余之间的细微差别。 实际上,您可以实现tail方法,因为您已经以大多数List实现的有效方式显示,并且LinkedList已经有一个getLast()方法。

peekLast方法已经在Deque接口中定义。
此外,deque必须具备这样的function。 因此,在List或任何其他界面中定义它是没有意义的。
分割function很方便。 如果您需要随机访问,那么您应该实现List 。 如果您需要有效地访问尾部,那么您应该实现Deque 。 您可以轻松实现它们(LinkedList实际上是这样做的)。

head()通过list.iterator()。next(),list.get(0)等提供。

如果列表与尾指针双重链接,或者基于数组等,则提供tail()是合理的。这些方面都没有为Lis​​t接口本身指定。 否则它可能具有O(N)性能。