我最近与同事讨论过为什么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方法不是一个好主意?
答案 0 :(得分:18)
Java Collections Framework由Joshua Bloch编写。他的API设计原则之一是:高功率重量比。
tail()
和head()
可由get()
和size()
实施,因此无需将tail()
和head()
添加到通用接口java.util.List
。一旦用户使用这些方法,您就没有机会删除它们,您必须永远保留这些不必要的方法。那很糟糕。
答案 1 :(得分:18)
列表界面的window
几乎为subList
和head
。你可以按如下方式包装它
tail
修改强>
根据@Pablo Grisafi的回答,这是一个Java快速排序实现 - 不是通用的,也不是高效的。正如预期的那样public List head(List list) {
return list.subList(0, 1);
}
public List tail(List list) {
return list.subList(1, list.size());
}
应该返回一个元素 - 而不是列表。
head()
答案 2 :(得分:4)
如果要递归处理列表(通常是函数式编程中使用的head / tail),可以使用迭代器。
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);
}
答案 3 :(得分:2)
据我所知,List
没有element
方法。但是,LinkedList
有getFirst()
和getLast()
,就像您描述的那样。
答案 4 :(得分:2)
在我的拙见中,尾巴和头部对具有功能背景的人更熟悉。当你开始传递函数时,它们是非常有用的,这就是大多数函数式语言实现它们的原因,甚至还有快捷符号来引用它们,比如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)看起来完全不同,它是低级别的,并且不依赖于头尾分离。
注意:下一句话是完全主观的,并且基于我的个人经验,可能被证明是错误的,但我认为这是真的:
函数式编程中的大多数算法都依赖于头/尾,在程序编程中,您依赖于访问给定位置的元素
答案 5 :(得分:1)
在API设计中必须始终做出选择。 可以添加到API中的批次方法,但是,您必须找到使API适用于大多数人并使其过于混乱的方法之间的界限。多余的。实际上,你可以实现tail方法,就像你为大多数List实现一样有效地展示了,而LinkedList已经有了一个getLast()方法。
答案 6 :(得分:0)
peekLast方法已经在Deque接口中定义 此外,deque必须具备这样的功能。因此,在List或任何其他界面中定义它是没有意义的 分割功能很方便。如果您需要随机访问,则应实施List。如果您需要有效地访问尾部,那么您应该实现Deque。您可以轻松实现它们(LinkedList实际上是这样做的。)
答案 7 :(得分:-1)
head()
通过list.iterator()。next(),list.get(0)等提供。
如果列表与尾指针双重链接,或者基于数组等,则提供tail()是合理的。这些方面都没有为List接口本身指定。否则它可能具有O(N)性能。