如何在 Java 中获得列表的反向列表视图?

发布于 2024-09-28 04:15:11 字数 990 浏览 2 评论 0原文

我想在列表上有一个反向列表视图(与 List#sublist 在列表上提供子列表视图类似)。是否有一些函数可以提供此功能?

我不想复制该列表,也不想修改该列表。

在这种情况下,如果我能在列表上至少获得一个反向迭代器就足够了。


另外,我知道如何自己实现这一点。我只是想问Java是否已经提供了这样的东西。

演示实现:

static <T> Iterable<T> iterableReverseList(final List<T> l) {
    return new Iterable<T>() {
        public Iterator<T> iterator() {
            return new Iterator<T>() {
                ListIterator<T> listIter = l.listIterator(l.size());                    
                public boolean hasNext() { return listIter.hasPrevious(); }
                public T next() { return listIter.previous(); }
                public void remove() { listIter.remove(); }                 
            };
        }
    };
}

我刚刚发现一些 List 实现具有 descendingIterator() 这正是我所需要的。尽管 List 没有通用的此类实现。这有点奇怪,因为我在 LinkedList 中看到的实现足够通用,可以与任何 List 一起使用。

I want to have a reversed list view on a list (in a similar way than List#sublist provides a sublist view on a list). Is there some function which provides this functionality?

I don't want to make any sort of copy of the list nor modify the list.

It would be enough if I could get at least a reverse iterator on a list in this case though.


Also, I know how to implement this myself. I'm just asking if Java already provides something like this.

Demo implementation:

static <T> Iterable<T> iterableReverseList(final List<T> l) {
    return new Iterable<T>() {
        public Iterator<T> iterator() {
            return new Iterator<T>() {
                ListIterator<T> listIter = l.listIterator(l.size());                    
                public boolean hasNext() { return listIter.hasPrevious(); }
                public T next() { return listIter.previous(); }
                public void remove() { listIter.remove(); }                 
            };
        }
    };
}

I just have found out that some List implementations have descendingIterator() which is what I need. Though there is no general such implementation for List. Which is kind of strange because the implementation I have seen in LinkedList is general enough to work with any List.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

柏拉图鍀咏恒 2024-10-05 04:15:12

更新 2024.06.16:

Emmanuel Bourg 发表精彩评论后,我又看了一眼查看代码并将其升级为现代风格的 null 安全,并纳入了他的出色建议。原始代码位于最后一条水平线下方。

它与最新版本的 Java 更加一致,即,利用 null 是一个类型漏洞,改变方法参数是一个类型漏洞代码味道,并且可变性被限制在一个很小的块内。尽可能在单个方法中编写代码,以提高 FP 可组合性(而不是较弱且容易出错的 OOP 可重用性)。


对于使用 Java 21 或更高版本的任何人,请使用 List.reversed()

对于使用 Java 8 到 Java 20 的任何人...

  1. 想要防范 null
  2. 不想修改传递的参数,List(强烈的代码味道)
  3. 想要返回不可修改的 List
  4. 不想使用像 Guava 这样的第三方库
  5. 想要一个遵循 Java Collection API Stream 的函数流畅的调用风格
  6. 想要看到现代风格的代码(并摆脱 20 世纪 90 年代末非常类似 C 的代码)
  7. 想要转向表达式(并远离语句)
  8. 想要尽可能避免重新实现(在此情况下)在这种情况下,通过调用面向突变的命令性方法 Collections.reverse()(已存在于 Java Collections API 中)

...这是我生成的一个有用的解决方案:

/**
 * Returns a {@link Collections#unmodifiableList} with the contained instances in reverse order.
 *
 * @param list the list to copy from while reversing ({@code null} safe)
 * @param <T> the type of instances contained in the {@link list}
 * @return a {@link Collections#unmodifiableList} with the contained instances in reverse order
 */
public static <T> List<T> reverse(List<T> list) {
  return Optional.ofNullable(list)
      .filter(listNonNull ->
         !listNonNull.isEmpty())
      .map(listNonEmpty -> {
        if (listNonEmpty.size() == 1) {
          return Collections.unmodifiableList(listNonEmpty);
        }

        var listNonEmptyCopy = new ArrayList<>(listNonEmpty);
        Collections.reverse(listNonEmptyCopy);

        return Collections.unmodifiableList(listNonEmptyCopy);
      })
      .orElse(List.of());
}

最初发布的代码 已在上面更新(请勿使用):

/**
 * Reverse the order of the instances returning an unmodifiable list.
 *
 * @param list the list to copy from while reversing
 * @param <T> the type of instances contained in the list
 * @return an unmodifiable list with the contained instances in reverse order
 */
public static <T> List<T> reverse(List<T> list) {
  if (list.size() > 1) {
    var size = list.size();

    return IntStream.range(0, size)
        .mapToObj(index -> list.get(size - 1 - index))
        .toList();
  } else if (list.size() == 1) {
    return list;
  }

  return List.of();
}

UPDATE 2024.06.16:

After an awesome comment from Emmanuel Bourg, I took another look at the code and upgraded it to be modern-style null safe, and incorporating his excellent suggestion. The original code is captured below the last horizontal rule.

It's considerably more aligned with the latest versions of Java, i.e., exploiting null is a type-hole, mutating a method parameter is a type-hole code smell, and mutability being restricted to as small a block of code within a single method as possible to improve FP composability (as opposed to the weaker and error-prone OOP reusability).


For anyone on Java 21 or later, use List.reversed().

For anyone on Java 8 through Java 20 who...

  1. Wants to guard against null
  2. Does not want to modify the passed parameter, List<T> (a strong code smell)
  3. Wants to return an unmodifiable List<T>
  4. Does not want to use a third-party library like Guava
  5. Wants a function that follows the Java Collection API Stream<T>'s fluent calling style
  6. Wants to see modern style code (and to move away from the very C-like code from the late 1990s)
  7. Wants to move towards expressions (and to move away from statements)
  8. Wants to avoid reimplementing as much as possible (in this case, by calling the mutation-oriented imperative method, Collections.reverse(), already present in the Java Collections API)

...here's a useful solution I've generated:

/**
 * Returns a {@link Collections#unmodifiableList} with the contained instances in reverse order.
 *
 * @param list the list to copy from while reversing ({@code null} safe)
 * @param <T> the type of instances contained in the {@link list}
 * @return a {@link Collections#unmodifiableList} with the contained instances in reverse order
 */
public static <T> List<T> reverse(List<T> list) {
  return Optional.ofNullable(list)
      .filter(listNonNull ->
         !listNonNull.isEmpty())
      .map(listNonEmpty -> {
        if (listNonEmpty.size() == 1) {
          return Collections.unmodifiableList(listNonEmpty);
        }

        var listNonEmptyCopy = new ArrayList<>(listNonEmpty);
        Collections.reverse(listNonEmptyCopy);

        return Collections.unmodifiableList(listNonEmptyCopy);
      })
      .orElse(List.of());
}

Originally Posted Code that was updated above (DO NOT USE):

/**
 * Reverse the order of the instances returning an unmodifiable list.
 *
 * @param list the list to copy from while reversing
 * @param <T> the type of instances contained in the list
 * @return an unmodifiable list with the contained instances in reverse order
 */
public static <T> List<T> reverse(List<T> list) {
  if (list.size() > 1) {
    var size = list.size();

    return IntStream.range(0, size)
        .mapToObj(index -> list.get(size - 1 - index))
        .toList();
  } else if (list.size() == 1) {
    return list;
  }

  return List.of();
}
最美不过初阳 2024-10-05 04:15:12

您还可以这样做:

static ArrayList<String> reverseReturn(ArrayList<String> alist)
{
   if(alist==null || alist.isEmpty())
   { 
       return null;
   }

   ArrayList<String> rlist = new ArrayList<>(alist);

   Collections.reverse(rlist);
   return rlist;
}

You can also do this:

static ArrayList<String> reverseReturn(ArrayList<String> alist)
{
   if(alist==null || alist.isEmpty())
   { 
       return null;
   }

   ArrayList<String> rlist = new ArrayList<>(alist);

   Collections.reverse(rlist);
   return rlist;
}
谁的新欢旧爱 2024-10-05 04:15:11

在列表上使用 .clone() 方法。它将返回一个浅表副本,这意味着它将包含指向相同对象的指针,因此您不必复制列表。然后只需使用集合即可。

因此,

Collections.reverse(list.clone());

如果您使用的是 List 并且无权访问 clone() 您可以使用 subList()

List<?> shallowCopy = list.subList(0, list.size());
Collections.reverse(shallowCopy);

Use the .clone() method on your List. It will return a shallow copy, meaning that it will contain pointers to the same objects, so you won't have to copy the list. Then just use Collections.

Ergo,

Collections.reverse(list.clone());

If you are using a List and don't have access to clone() you can use subList():

List<?> shallowCopy = list.subList(0, list.size());
Collections.reverse(shallowCopy);
允世 2024-10-05 04:15:11

Guava 提供了以下内容:Lists.reverse(List)

List<String> letters = ImmutableList.of("a", "b", "c");
List<String> reverseView = Lists.reverse(letters); 
System.out.println(reverseView); // [c, b, a]

集合不同。相反,这纯粹是一个视图...它不会改变原始列表中元素的顺序。此外,对于可修改的原始列表,对原始列表和视图的更改都会反映在另一个列表中。

Guava provides this: Lists.reverse(List)

List<String> letters = ImmutableList.of("a", "b", "c");
List<String> reverseView = Lists.reverse(letters); 
System.out.println(reverseView); // [c, b, a]

Unlike Collections.reverse, this is purely a view... it doesn't alter the ordering of elements in the original list. Additionally, with an original list that is modifiable, changes to both the original list and the view are reflected in the other.

心清如水 2024-10-05 04:15:11

如果我理解正确,那么它就是一行代码。它对我有用。

 Collections.reverse(yourList);

If i have understood correct then it is one line of code .It worked for me .

 Collections.reverse(yourList);
海风掠过北极光 2024-10-05 04:15:11

它并不完全优雅,但如果你使用 List.listIterator(int index) ,你可以得到一个到列表末尾的双向 ListIterator :

//Assume List<String> foo;
ListIterator li = foo.listIterator(foo.size());

while (li.hasPrevious()) {
   String curr = li.previous();
}

Its not exactly elegant, but if you use List.listIterator(int index) you can get a bi-directional ListIterator to the end of the list:

//Assume List<String> foo;
ListIterator li = foo.listIterator(foo.size());

while (li.hasPrevious()) {
   String curr = li.previous();
}
遮了一弯 2024-10-05 04:15:11

我用这个:

public class ReversedView<E> extends AbstractList<E>{

    public static <E> List<E> of(List<E> list) {
        return new ReversedView<>(list);
    }

    private final List<E> backingList;

    private ReversedView(List<E> backingList){
        this.backingList = backingList;
    }

    @Override
    public E get(int i) {
        return backingList.get(backingList.size()-i-1);
    }

    @Override
    public int size() {
        return backingList.size();
    }

}

像这样:

ReversedView.of(backingList) // is a fully-fledged generic (but read-only) list

I use this:

public class ReversedView<E> extends AbstractList<E>{

    public static <E> List<E> of(List<E> list) {
        return new ReversedView<>(list);
    }

    private final List<E> backingList;

    private ReversedView(List<E> backingList){
        this.backingList = backingList;
    }

    @Override
    public E get(int i) {
        return backingList.get(backingList.size()-i-1);
    }

    @Override
    public int size() {
        return backingList.size();
    }

}

like this:

ReversedView.of(backingList) // is a fully-fledged generic (but read-only) list
丶视觉 2024-10-05 04:15:11

从 Java 21 开始,出现了一个新接口 SequencedCollection (它是 <代码>列表)。它具有方法 reversed() 提供集合的逆序视图:

jshell> List.of(1, 2, 3, 4, 5).reversed()
$1 ==> [5, 4, 3, 2, 1]

Since Java 21 there is a new interface SequencedCollection (which is a superinteface for List). It has the method reversed() which provides a reverse-ordered view of a collection:

jshell> List.of(1, 2, 3, 4, 5).reversed()
$1 ==> [5, 4, 3, 2, 1]
春庭雪 2024-10-05 04:15:11

java.util.DequedescendingIterator() - 如果您的 ListDeque,您可以使用它。

java.util.Deque has descendingIterator() - if your List is a Deque, you can use that.

心欲静而疯不止 2024-10-05 04:15:11

Collections.reverse(nums) ...它实际上颠倒了元素的顺序。
下面的代码应该非常感激 -

List<Integer> nums = new ArrayList<Integer>();
nums.add(61);
nums.add(42);
nums.add(83);
nums.add(94);
nums.add(15);
//Tosort the collections uncomment the below line
//Collections.sort(nums); 

Collections.reverse(nums);

System.out.println(nums);

输出:15,94,83,42,61

Collections.reverse(nums) ... It actually reverse the order of the elements.
Below code should be much appreciated -

List<Integer> nums = new ArrayList<Integer>();
nums.add(61);
nums.add(42);
nums.add(83);
nums.add(94);
nums.add(15);
//Tosort the collections uncomment the below line
//Collections.sort(nums); 

Collections.reverse(nums);

System.out.println(nums);

Output: 15,94,83,42,61

回首观望 2024-10-05 04:15:11

我知道这是一篇旧文章,但今天我正在寻找类似的内容。最后我自己写了代码:

private List reverseList(List myList) {
    List invertedList = new ArrayList();
    for (int i = myList.size() - 1; i >= 0; i--) {
        invertedList.add(myList.get(i));
    }
    return invertedList;
}

不推荐长列表,这根本没有优化。对于受控场景来说,这是一种简单的解决方案(我处理的列表不超过 100 个元素)。

希望它对某人有帮助。

I know this is an old post but today I was looking for something like this. In the end I wrote the code myself:

private List reverseList(List myList) {
    List invertedList = new ArrayList();
    for (int i = myList.size() - 1; i >= 0; i--) {
        invertedList.add(myList.get(i));
    }
    return invertedList;
}

Not recommended for long Lists, this is not optimized at all. It's kind of an easy solution for controlled scenarios (the Lists I handle have no more than 100 elements).

Hope it helps somebody.

燕归巢 2024-10-05 04:15:11

由于 Java 21 使用 List.reversed

返回此集合的逆序视图。相遇顺序
返回视图中元素的数量与遇到顺序相反
该集合中的元素。反向排序会影响所有
顺序敏感的操作,包括对视图集合的操作
返回的视图。如果收集实施允许
对此视图的修改,修改“直写”到
底层集合。对基础集合的更改可能或
在此反向视图中可能不可见,具体取决于
实施。

所以你对原始列表有一个相反的看法。

Since Java 21 use List.reversed.

Returns a reverse-ordered view of this collection. The encounter order
of elements in the returned view is the inverse of the encounter order
of elements in this collection. The reverse ordering affects all
order-sensitive operations, including those on the view collections of
the returned view. If the collection implementation permits
modifications to this view, the modifications "write through" to the
underlying collection. Changes to the underlying collection might or
might not be visible in this reversed view, depending upon the
implementation.

So you have a reversed view of the original list.

囍笑 2024-10-05 04:15:11

您还可以在请求对象时反转位置:

Object obj = list.get(list.size() - 1 - position);

You can also invert the position when you request an object:

Object obj = list.get(list.size() - 1 - position);
川水往事 2024-10-05 04:15:11

对于小型列表,我们可以创建 LinkedList,然后可以使用降序迭代器,如下所示:

List<String> stringList = new ArrayList<>(Arrays.asList("One", "Two", "Three"));
stringList.stream().collect(Collectors.toCollection(LinkedList::new))
         .descendingIterator().
         forEachRemaining(System.out::println); // Three, Two, One
System.out.println(stringList); // One, Two, Three

For small sized list we can create LinkedList and then can make use of descending iterator as:

List<String> stringList = new ArrayList<>(Arrays.asList("One", "Two", "Three"));
stringList.stream().collect(Collectors.toCollection(LinkedList::new))
         .descendingIterator().
         forEachRemaining(System.out::println); // Three, Two, One
System.out.println(stringList); // One, Two, Three
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文