给定一个包含21个元素的Java List。
使用以下方法创建三个新列表的最佳方法是什么:
A = 0, 3, 6, ... indexed elements from source
B = 1, 4, 7, ...
C = 2 ,5, 8, 11, 14, 17, 20
没有循环可以吗?
答案 0 :(得分:6)
你可以编写一个包装类,它能够在给定倍数(在这种情况下为3)和偏移量(0,1和2)的列表上提供只读“视图” )。当在特定索引处询问项目时,它必须乘以“倍数”并添加偏移量,然后查看原始列表。 (同样适用于其他操作。)
循环虽然更简单......这里的背景是什么?你真正想要实现的是什么?
答案 1 :(得分:2)
这里是Jon提到的一个例子(当然,你真的不想循环)。这个名字并不是很好......我不确定这样一个好名字是什么。
public class OffsetList<E> extends AbstractList<E> {
private final List<E> delegate;
private final int offset;
private final int multiple;
public static <E> OffsetList<E> create(List<E> delegate, int offset, int multiple) {
return new OffsetList<E>(delegate, offset, multiple);
}
private OffsetList(List<E> delegate, int offset, int multiple) {
this.delegate = delegate;
this.offset = offset;
this.multiple= multiple;
}
@Override public E get(int index) {
return delegate.get(offset + (index * multiple));
}
@Override public int size() {
int offsetToEnd = delegate.size() - offset;
return (int) Math.ceil(offsetToEnd / (double) multiple);
}
}
使用示例:
List<Integer> numbers = // the numbers 0 to 21 in order
List<Integer> first = OffsetList.create(numbers, 0, 3);
List<Integer> second = OffsetList.create(numbers, 1, 3);
List<Integer> third = OffsetList.create(numbers, 2, 3);
System.out.println(first); // [0, 3, 6, 9, 12, 15, 18, 21]
System.out.println(second); // [1, 4, 7, 10, 13, 16, 19]
System.out.println(third); // [2, 5, 8, 11, 14, 17, 20]
创建每个列表是O(1),因为它们是视图。迭代每个列表是O(n),其中n是实际视图列表的大小,而不是它所基于的完整列表的大小。这假设原始列表是随机访问列表......这种方法与基于索引的迭代一样,对于链表而言效率非常低。
答案 2 :(得分:1)
鉴于你说你已经习惯了函数式编程,我会假设你想要分割列表,因为你想要做不同的东西。如果是这种情况,我会将过滤逻辑放在Iterator
级别。
您可以使用包装 Iterator 而不是包装List
。它可能看起来像这样:
public <T> Iterable<T> filter(final Iterable<T> allElements, final int offset, final int multiple) {
return new Iterable<T> {
public Iterator<T> iterator() {
return new Iterator<T> {
int index = 0;
Iterator<T> allElementsIt = allElements.iterator();
public boolean hasNext() {
while (allElementsIt.hasNext()) {
if ( isDesiredIndex(index) ) {
return true;
} else {
allElementsIt.next();
index++;
}
}
return false;
}
private boolean isDesiredIndex(int index) {
return (index - offset) % multiple == 0;
}
public T next() {
if ( hasNext() ) {
return allElementsIt.next();
} else {
throw NoSuchElementException();
}
}
public void remove() {...}
}
}
}
}
然后使用它:
for ( ElementType element : filter(elements, 2, 3) ) {
//do something to every third starting with third element
}
答案 3 :(得分:-1)
接下来尝试:)
class Mod3Comparator implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
if (a % 3 < b % 3 || (a % 3 == b % 3 && a < b)) {
return -1;
}
if (a % 3 > b % 3 || (a % 3 == b % 3 && a > b)) {
return 1;
}
return 0;
}
}
首先根据模数规则对列表进行排序,然后使用Arrays.copyOfRange
方法。
Collections.sort(list, new Mod3Comparator());
Integer[] array = new Integer[list.size()];
list.toArray(array);
List<Integer> A = Arrays.asList(Arrays.copyOfRange(array, 0, 7));
List<Integer> B = Arrays.asList(Arrays.copyOfRange(array, 7, 14));
...
另见example。
答案 4 :(得分:-2)
不幸的是,我无法想到这样做的方式而不假装数组列表正在执行以下操作。
String[] twentyOne = new String[21];
String[] first = new String[3];
first[0] = twentyOne[0];
first[1] = twentyOne[3];
first[2] = twentyOne[6];
// And so on
String[] second = new String[3];
second[0] = twentyOne[1];
second[1] = twentyOne[4];
second[2] = twentyOne[7];
String[] third = new String[15];
third[0] = twentyOne[2];
// You get the picture
我在示例中只使用了数组,因为我对它们更有信心,并且无需查看内容即可了解它们。
我可以问你为什么要避免循环?